1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-22 04:17:03 +00:00

Publish the new version ✈️ | Look description please!

# FAQ

### Looking for free support?

	http://support.iris-go.com
    https://kataras.rocket.chat/channel/iris

### Looking for previous versions?

    https://github.com/kataras/iris#version

### Should I upgrade my Iris?

Developers are not forced to upgrade if they don't really need it. Upgrade whenever you feel ready.
> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes.

**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
For further installation support, please click [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework).

### About our new home page
    http://iris-go.com

Thanks to [Santosh Anand](https://github.com/santoshanand) the http://iris-go.com has been upgraded and it's really awesome!

[Santosh](https://github.com/santoshanand) is a freelancer, he has a great knowledge of nodejs and express js, Android, iOS, React Native, Vue.js etc, if you need a developer to find or create a solution for your problem or task, please contact with him.

The amount of the next two or three donations you'll send they will be immediately transferred to his own account balance, so be generous please!

Read more at https://github.com/kataras/iris/blob/master/HISTORY.md


Former-commit-id: eec2d71bbe011d6b48d2526eb25919e36e5ad94e
This commit is contained in:
kataras
2017-06-03 23:22:52 +03:00
parent 03bcadadec
commit 5e4b63acb2
330 changed files with 35786 additions and 17316 deletions

27
typescript/LICENSE Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Gerasimos Maropoulos nor the name of his
username, kataras, may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

196
typescript/config.go Normal file
View File

@@ -0,0 +1,196 @@
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package typescript
import (
"encoding/json"
"io/ioutil"
"os"
"reflect"
"strconv"
"github.com/kataras/iris/typescript/npm"
)
var (
pathSeparator = string(os.PathSeparator)
nodeModules = pathSeparator + "node_modules" + pathSeparator
)
type (
// Tsconfig the struct for tsconfig.json
Tsconfig struct {
CompilerOptions CompilerOptions `json:"compilerOptions"`
Exclude []string `json:"exclude"`
}
// CompilerOptions contains all the compiler options used by the tsc (typescript compiler)
CompilerOptions struct {
Declaration bool `json:"declaration"`
Module string `json:"module"`
Target string `json:"target"`
Watch bool `json:"watch"`
Charset string `json:"charset"`
Diagnostics bool `json:"diagnostics"`
EmitBOM bool `json:"emitBOM"`
EmitDecoratorMetadata bool `json:"emitDecoratorMetadata"`
ExperimentalDecorators bool `json:"experimentalDecorators"`
InlineSourceMap bool `json:"inlineSourceMap"`
InlineSources bool `json:"inlineSources"`
IsolatedModules bool `json:"isolatedModules"`
Jsx string `json:"jsx"`
ReactNamespace string `json:"reactNamespace"`
ListFiles bool `json:"listFiles"`
Locale string `json:"locale"`
MapRoot string `json:"mapRoot"`
ModuleResolution string `json:"moduleResolution"`
NewLine string `json:"newLine"`
NoEmit bool `json:"noEmit"`
NoEmitOnError bool `json:"noEmitOnError"`
NoEmitHelpers bool `json:"noEmitHelpers"`
NoImplicitAny bool `json:"noImplicitAny"`
NoLib bool `json:"noLib"`
NoResolve bool `json:"noResolve"`
SkipDefaultLibCheck bool `json:"skipDefaultLibCheck"`
OutDir string `json:"outDir"`
OutFile string `json:"outFile"`
PreserveConstEnums bool `json:"preserveConstEnums"`
Pretty bool `json:"pretty"`
RemoveComments bool `json:"removeComments"`
RootDir string `json:"rootDir"`
SourceMap bool `json:"sourceMap"`
SourceRoot string `json:"sourceRoot"`
StripInternal bool `json:"stripInternal"`
SuppressExcessPropertyErrors bool `json:"suppressExcessPropertyErrors"`
SuppressImplicitAnyIndexErrors bool `json:"suppressImplicitAnyIndexErrors"`
AllowUnusedLabels bool `json:"allowUnusedLabels"`
NoImplicitReturns bool `json:"noImplicitReturns"`
NoFallthroughCasesInSwitch bool `json:"noFallthroughCasesInSwitch"`
AllowUnreachableCode bool `json:"allowUnreachableCode"`
ForceConsistentCasingInFileNames bool `json:"forceConsistentCasingInFileNames"`
AllowSyntheticDefaultImports bool `json:"allowSyntheticDefaultImports"`
AllowJs bool `json:"allowJs"`
NoImplicitUseStrict bool `json:"noImplicitUseStrict"`
}
// Config the configs for the Typescript plugin
// Has five (5) fields
//
// 1. Bin: string, the typescript installation directory/typescript/lib/tsc.js, if empty it will search inside global npm modules
// 2. Dir: string, Dir set the root, where to search for typescript files/project. Default "./"
// 3. Ignore: string, comma separated ignore typescript files/project from these directories. Default "" (node_modules are always ignored)
// 4. Tsconfig: &typescript.Tsconfig{}, here you can set all compilerOptions if no tsconfig.json exists inside the 'Dir'
// 5. Editor: typescript.Editor("username","password"), if setted then alm-tools browser-based typescript IDE will be available. Defailt is nil
Config struct {
// Bin the path of the tsc binary file
// if empty then the plugin tries to find it
Bin string
// Dir the client side directory, which typescript (.ts) files are live
Dir string
// Ignore ignore folders, default is /node_modules/
Ignore string
// Tsconfig the typescript build configs, including the compiler's options
Tsconfig *Tsconfig
}
)
// CompilerArgs returns the CompilerOptions' contents of the Tsconfig
// it reads the json tags, add '--' at the start of each one and returns an array of strings
// this is from file
func (tsconfig *Tsconfig) CompilerArgs() []string {
val := reflect.ValueOf(tsconfig).Elem().FieldByName("CompilerOptions") // -> for tsconfig *Tsconfig
// val := reflect.ValueOf(tsconfig.CompilerOptions)
compilerOpts := make([]string, 0) // 0 because we don't know the real valid options yet.
for i := 0; i < val.NumField(); i++ {
typeField := val.Type().Field(i)
valueFieldG := val.Field(i)
var valueField string
// only if it's string or int we need to put that
if valueFieldG.Kind() == reflect.String {
//if valueFieldG.String() != "" {
//valueField = strconv.QuoteToASCII(valueFieldG.String())
// }
valueField = valueFieldG.String()
} else if valueFieldG.Kind() == reflect.Int {
if valueFieldG.Int() > 0 {
valueField = strconv.Itoa(int(valueFieldG.Int()))
}
} else if valueFieldG.Kind() == reflect.Bool {
valueField = strconv.FormatBool(valueFieldG.Bool())
}
if valueField != "" && valueField != "false" {
// var opt string
// key := typeField.Tag.Get("json")
// // it's bool value of true then just --key, for example --watch
// if valueField == "true" {
// opt = "--" + key
// } else {
// // it's a string now, for example -m commonjs
// opt = "-" + string(key[0]) + " " + valueField
// }
key := "--" + typeField.Tag.Get("json")
compilerOpts = append(compilerOpts, key)
// the form is not '--module ES6' but os.Exec should recognise them as arguments
// so we need to put the values on the next index
if valueField != "true" {
// it's a string now, for example -m commonjs
compilerOpts = append(compilerOpts, valueField)
}
}
}
return compilerOpts
}
// FromFile reads a file & returns the Tsconfig by its contents
func FromFile(tsConfigAbsPath string) (config Tsconfig, err error) {
file, err := ioutil.ReadFile(tsConfigAbsPath)
if err != nil {
return
}
config = Tsconfig{}
err = json.Unmarshal(file, &config)
return
}
// DefaultTsconfig returns the default Tsconfig, with CompilerOptions module: commonjs, target: es5 and ignore the node_modules
func DefaultTsconfig() Tsconfig {
return Tsconfig{
CompilerOptions: CompilerOptions{
Module: "commonjs",
Target: "ES6",
Jsx: "react",
ModuleResolution: "classic",
Locale: "en",
Watch: true,
NoImplicitAny: false,
SourceMap: false,
},
Exclude: []string{"node_modules"},
}
}
// DefaultConfig returns the default Options of the Typescript adaptor
// Bin and Editor are setting in runtime via the adaptor
func DefaultConfig() Config {
root, err := os.Getwd()
if err != nil {
panic("Typescript Adaptor: Cannot get the Current Working Directory !!! [os.getwd()]")
}
compilerTsConfig := DefaultTsconfig()
c := Config{
Dir: root + pathSeparator,
Ignore: nodeModules,
Tsconfig: &compilerTsConfig,
}
c.Bin = npm.NodeModuleAbs("typescript/lib/tsc.js")
return c
}

51
typescript/editor/LICENSE Normal file
View File

@@ -0,0 +1,51 @@
Copyright (c) 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Gerasimos Maropoulos nor the name of his
username, kataras, may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Third-Parties:
The MIT License (MIT)
Copyright (c) 2016 Basarat Ali Syed
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,50 @@
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package editor
import (
"os"
)
// Default values for the configuration
const (
DefaultPort = 4444
)
// Config the configs for the Editor plugin
type Config struct {
// Hostname if empty used the iris server's hostname
Hostname string
// Port if 0 4444
Port int
// KeyFile the key file(ssl optional)
KeyFile string
// CertFile the cert file (ssl optional)
CertFile string
// WorkingDir if empty "./"
WorkingDir string
// Username defaults to empty, you should set this
Username string
// Password defaults to empty, you should set this
Password string
// DisableOutput set that to true if you don't care about alm-tools' messages
// they are useful because that the default value is "false"
DisableOutput bool
}
// DefaultConfig returns the default configs for the Editor plugin
func DefaultConfig() Config {
// explicit
return Config{
Hostname: "",
Port: 4444,
KeyFile: "",
CertFile: "",
WorkingDir: "." + string(os.PathSeparator), // alm-tools should end with path separator.
Username: "",
Password: "",
DisableOutput: false,
}
}

222
typescript/editor/editor.go Normal file
View File

@@ -0,0 +1,222 @@
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package editor
// +------------------------------------------------------------+
// | Editor usage |
// +------------------------------------------------------------+
//
// import "github.com/kataras/iris/typescript/editor"
// [...]
//
// app := iris.New()
// e := editor.New(editor.Config{})
// e.Attach(app)
//
// [...]
// app.Run(iris.Addr(":8080"))
//
// +------------------------------------------------------------+
// | General notes for authentication |
// +------------------------------------------------------------+
//
// The Authorization specifies the authentication mechanism (in this case Basic) followed by the username and password.
// Although, the string aHR0cHdhdGNoOmY= may look encrypted it is simply a base64 encoded version of <username>:<password>.
// Would be readily available to anyone who could intercept the HTTP request.
import (
"bufio"
"io"
"os"
"path/filepath"
"strconv"
"github.com/kataras/iris"
"github.com/kataras/iris/core/host"
"github.com/kataras/iris/typescript/npm"
)
type (
// Editor is the alm-tools adaptor.
//
// It holds a logger from the iris' station
// username,password for basic auth
// directory which the client side code is
// keyfile,certfile for TLS listening
// and a host which is listening for
Editor struct {
config *Config
log func(format string, a ...interface{})
enabled bool // default true
// after alm started
process *os.Process
debugOutput io.Writer
}
)
// New creates and returns an Editor Plugin instance
func New(cfg ...Config) *Editor {
c := DefaultConfig()
if len(cfg) > 0 {
c = cfg[0]
}
c.WorkingDir = validateWorkingDir(c.WorkingDir) // add "/" if not exists
return &Editor{
enabled: true,
config: &c,
}
}
// User set a user, accepts two parameters: username (string), string (string)
func (e *Editor) User(username string, password string) *Editor {
e.config.Username = username
e.config.Password = password
return e
}
func validateWorkingDir(workingDir string) string {
l := workingDir[len(workingDir)-1]
if l != '/' && l != os.PathSeparator {
workingDir += "/"
}
return workingDir
}
// Dir sets the directory which the client side source code alive
func (e *Editor) Dir(workingDir string) *Editor {
e.config.WorkingDir = validateWorkingDir(workingDir)
return e
}
// Port sets the port (int) for the editor adaptor's standalone server
func (e *Editor) Port(port int) *Editor {
e.config.Port = port
return e
}
// SetEnable if true enables the editor adaptor, otherwise disables it
func (e *Editor) SetEnable(enable bool) {
e.enabled = enable
}
// DisableOutput call that if you don't care about alm-tools' messages
// they are useful because that the default configuration shows them
func (e *Editor) DisableOutput() {
e.config.DisableOutput = true
}
// GetDescription EditorPlugin is a bridge between Iris and the alm-tools, the browser-based IDE for client-side sources.
func (e *Editor) GetDescription() string {
return "A bridge between Iris and the alm-tools, the browser-based IDE."
}
// we use that editorWriter to prefix the editor's output with "Editor Adaptor: "
type editorWriter struct {
underline io.Writer
}
// build runs before the server's listens, creates the listener ( use of port parent hostname:DefaultPort if not exist)
func (e *Editor) build(s *iris.Application) {
e.log = s.Log
if e.config.Hostname == "" {
e.config.Hostname = "0.0.0.0"
}
if e.config.Port <= 0 {
e.config.Port = DefaultPort
}
if s, err := filepath.Abs(e.config.WorkingDir); err == nil {
e.config.WorkingDir = s
}
e.start()
}
// close kills the editor's server when Iris is closed
func (e *Editor) close(s *iris.Application) {
if e.process != nil {
err := e.process.Kill()
if err != nil {
e.log("error while trying to terminate the Editor,please kill this process by yourself, process id: %d", e.process.Pid)
}
}
}
// start starts the job
func (e *Editor) start() {
if e.config.Username == "" || e.config.Password == "" {
e.log("error before running alm-tools. You have to set username & password for security reasons, otherwise this adaptor won't run.")
return
}
if !npm.NodeModuleExists("alm/bin/alm") {
e.log("installing alm-tools, please wait...")
res := npm.NodeModuleInstall("alm")
if res.Error != nil {
e.log(res.Error.Error())
return
}
e.log(res.Message)
}
cmd := npm.CommandBuilder("node", npm.NodeModuleAbs("alm/src/server.js"))
cmd.AppendArguments("-a", e.config.Username+":"+e.config.Password,
"-h", e.config.Hostname, "-t", strconv.Itoa(e.config.Port), "-d", e.config.WorkingDir)
// for auto-start in the browser: cmd.AppendArguments("-o")
if e.config.KeyFile != "" && e.config.CertFile != "" {
cmd.AppendArguments("--httpskey", e.config.KeyFile, "--httpscert", e.config.CertFile)
}
prefix := ""
// when debug is not disabled
// show any messages to the user( they are useful here)
// to the io.Writer that iris' user is defined from configuration
if !e.config.DisableOutput {
outputReader, err := cmd.StdoutPipe()
if err == nil {
outputScanner := bufio.NewScanner(outputReader)
go func() {
for outputScanner.Scan() {
e.log(prefix + outputScanner.Text())
}
}()
errReader, err := cmd.StderrPipe()
if err == nil {
errScanner := bufio.NewScanner(errReader)
go func() {
for errScanner.Scan() {
e.log(prefix + errScanner.Text())
}
}()
}
}
}
err := cmd.Start()
if err != nil {
e.log(prefix + err.Error())
return
}
// no need, alm-tools post these
// e.logger.Printf("Editor is running at %s:%d | %s", e.config.Hostname, e.config.Port, e.config.WorkingDir)
}
// Attach adapts the editor to one or more Iris instance(s).
func (e *Editor) Attach(app *iris.Application) {
e.build(app)
app.Scheduler.Schedule(host.OnInterrupt(func(proc host.TaskProcess) {
e.close(app)
}))
}

118
typescript/npm/exec.go Normal file
View File

@@ -0,0 +1,118 @@
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package npm // #nosec
import (
"fmt"
"os"
"os/exec"
"strings"
)
var (
// PathSeparator is the string of os.PathSeparator
PathSeparator = string(os.PathSeparator)
)
type (
// Cmd is a custom struch which 'implements' the *exec.Cmd
Cmd struct {
*exec.Cmd
}
)
// Arguments sets the command line arguments, including the command as Args[0].
// If the args parameter is empty or nil, Run uses {Path}.
//
// In typical use, both Path and args are set by calling Command.
func (cmd *Cmd) Arguments(args ...string) *Cmd {
cmd.Cmd.Args = append(cmd.Cmd.Args[0:1], args...) //we need the first argument which is the command
return cmd
}
// AppendArguments appends the arguments to the exists
func (cmd *Cmd) AppendArguments(args ...string) *Cmd {
cmd.Cmd.Args = append(cmd.Cmd.Args, args...)
return cmd
}
// ResetArguments resets the arguments
func (cmd *Cmd) ResetArguments() *Cmd {
cmd.Args = cmd.Args[0:1] //keep only the first because is the command
return cmd
}
// Directory sets the working directory of the command.
// If workingDirectory is the empty string, Run runs the command in the
// calling process's current directory.
func (cmd *Cmd) Directory(workingDirectory string) *Cmd {
cmd.Cmd.Dir = workingDirectory
return cmd
}
// CommandBuilder creates a Cmd object and returns it
// accepts 2 parameters, one is optionally
// first parameter is the command (string)
// second variatic parameter is the argument(s) (slice of string)
//
// the difference from the normal Command function is that you can re-use this Cmd, it doesn't execute until you call its Command function
func CommandBuilder(command string, args ...string) *Cmd {
return &Cmd{Cmd: exec.Command(command, args...)}
}
//the below is just for exec.Command:
// Command executes a command in shell and returns it's output, it's block version
func Command(command string, a ...string) (output string, err error) {
var out []byte
//if no args given, try to get them from the command
if len(a) == 0 {
commandArgs := strings.Split(command, " ")
for _, commandArg := range commandArgs {
if commandArg[0] == '-' { // if starts with - means that this is an argument, append it to the arguments
a = append(a, commandArg)
}
}
}
out, err = exec.Command(command, a...).Output()
if err == nil {
output = string(out)
}
return
}
// MustCommand executes a command in shell and returns it's output, it's block version. It panics on an error
func MustCommand(command string, a ...string) (output string) {
var out []byte
var err error
if len(a) == 0 {
commandArgs := strings.Split(command, " ")
for _, commandArg := range commandArgs {
if commandArg[0] == '-' { // if starts with - means that this is an argument, append it to the arguments
a = append(a, commandArg)
}
}
}
out, err = exec.Command(command, a...).Output()
if err != nil {
argsToString := strings.Join(a, " ")
panic(fmt.Sprintf("\nError running the command %s", command+" "+argsToString))
}
output = string(out)
return
}
// Exists returns true if directory||file exists
func Exists(dir string) bool {
if _, err := os.Stat(dir); os.IsNotExist(err) {
return false
}
return true
}

128
typescript/npm/npm.go Normal file
View File

@@ -0,0 +1,128 @@
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package npm
import (
"fmt"
"strings"
"time"
)
var (
// nodeModulesPath is the path of the root npm modules
// Ex: C:\\Users\\kataras\\AppData\\Roaming\\npm\\node_modules
nodeModulesPath string
)
type (
// NodeModuleResult holds Message and Error, if error != nil then the npm command has failed
NodeModuleResult struct {
// Message the message (string)
Message string
// Error the error (if any)
Error error
}
)
// NodeModulesPath sets the root directory for the node_modules and returns that
func NodeModulesPath() string {
if nodeModulesPath == "" {
nodeModulesPath = MustCommand("npm", "root", "-g") //here it ends with \n we have to remove it
nodeModulesPath = nodeModulesPath[0 : len(nodeModulesPath)-1]
}
return nodeModulesPath
}
func success(output string, a ...interface{}) NodeModuleResult {
return NodeModuleResult{fmt.Sprintf(output, a...), nil}
}
func fail(errMsg string, a ...interface{}) NodeModuleResult {
return NodeModuleResult{"", fmt.Errorf("\n"+errMsg, a...)}
}
// Output returns the error message if result.Error exists, otherwise returns the result.Message
func (res NodeModuleResult) Output() (out string) {
if res.Error != nil {
out = res.Error.Error()
} else {
out = res.Message
}
return
}
// NodeModuleInstall installs a module
func NodeModuleInstall(moduleName string) NodeModuleResult {
finish := make(chan bool)
go func() {
print("\n|")
print("_")
print("|")
for {
select {
case v := <-finish:
{
if v {
print("\010\010\010") //remove the loading chars
close(finish)
return
}
}
default:
print("\010\010-")
time.Sleep(time.Second / 2)
print("\010\\")
time.Sleep(time.Second / 2)
print("\010|")
time.Sleep(time.Second / 2)
print("\010/")
time.Sleep(time.Second / 2)
print("\010-")
time.Sleep(time.Second / 2)
print("|")
}
}
}()
out, err := Command("npm", "install", moduleName, "-g")
finish <- true
if err != nil {
return fail("Error installing module %s. Trace: %s", moduleName, err.Error())
}
return success("\n%s installed %s", moduleName, out)
}
// NodeModuleUnistall removes a module
func NodeModuleUnistall(moduleName string) NodeModuleResult {
out, err := Command("npm", "unistall", "-g", moduleName)
if err != nil {
return fail("Error unstalling module %s. Trace: %s", moduleName, err.Error())
}
return success("\n %s unistalled %s", moduleName, out)
}
// NodeModuleAbs returns the absolute path of the global node_modules directory + relative
func NodeModuleAbs(relativePath string) string {
return NodeModulesPath() + PathSeparator + strings.Replace(relativePath, "/", PathSeparator, -1)
}
// NodeModuleExists returns true if a module exists
// here we have two options
//1 . search by command something like npm -ls -g --depth=x
//2. search on files, we choose the second
func NodeModuleExists(executableRelativePath string) bool {
execAbsPath := NodeModuleAbs(executableRelativePath)
if execAbsPath == "" {
return false
}
return Exists(execAbsPath)
}

229
typescript/typescript.go Normal file
View File

@@ -0,0 +1,229 @@
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package typescript provides a typescript compiler with hot-reloader
// and optionally a cloud-based editor, called 'alm-tools'.
// typescript (by microsoft) and alm-tools (by @basarat) have their own (open-source) licenses
// the tools are not used directly by this adaptor, but it's good to know where you can find
// the software.
package typescript
import (
"errors"
"os"
"path/filepath"
"strings"
"github.com/kataras/iris"
"github.com/kataras/iris/typescript/npm"
)
type (
// Typescript contains the unique iris' typescript loader, holds all necessary fields & methods.
Typescript struct {
Config *Config
// taken from framework
log func(format string, a ...interface{})
}
)
// New creates & returns a new instnace typescript plugin
func New() *Typescript {
c := DefaultConfig()
if !strings.Contains(c.Ignore, nodeModules) {
c.Ignore += "," + nodeModules
}
return &Typescript{Config: &c}
}
// implementation
func (t *Typescript) start() {
if t.hasTypescriptFiles() {
//Can't check if permission denied returns always exists = true....
if !npm.NodeModuleExists(t.Config.Bin) {
t.log("installing typescript, please wait...")
res := npm.NodeModuleInstall("typescript")
if res.Error != nil {
t.log(res.Error.Error())
return
}
t.log(res.Message)
}
projects := t.getTypescriptProjects()
if len(projects) > 0 {
watchedProjects := 0
//typescript project (.tsconfig) found
for _, project := range projects {
cmd := npm.CommandBuilder("node", t.Config.Bin, "-p", project[0:strings.LastIndex(project, npm.PathSeparator)]) //remove the /tsconfig.json)
projectConfig, perr := FromFile(project)
if perr != nil {
t.log("error while trying to read tsconfig: %s", perr.Error())
continue
}
if projectConfig.CompilerOptions.Watch {
watchedProjects++
// if has watch : true then we have to wrap the command to a goroutine (I don't want to use the .Start here)
go func() {
_, err := cmd.Output()
if err != nil {
t.log(err.Error())
return
}
}()
} else {
_, err := cmd.Output()
if err != nil {
t.log(err.Error())
return
}
}
}
// t.log("%d Typescript project(s) compiled ( %d monitored by a background file watcher", len(projects), watchedProjects)
} else {
//search for standalone typescript (.ts) files and compile them
files := t.getTypescriptFiles()
if len(files) > 0 {
/* watchedFiles := 0
if t.Config.Tsconfig.CompilerOptions.Watch {
watchedFiles = len(files)
}*/
//it must be always > 0 if we came here, because of if hasTypescriptFiles == true.
for _, file := range files {
absPath, err := filepath.Abs(file)
if err != nil {
continue
}
//these will be used if no .tsconfig found.
// cmd := npm.CommandBuilder("node", t.Config.Bin)
// cmd.Arguments(t.Config.Bin, t.Config.Tsconfig.CompilerArgs()...)
// cmd.AppendArguments(absPath)
compilerArgs := t.Config.Tsconfig.CompilerArgs()
cmd := npm.CommandBuilder("node", t.Config.Bin)
for _, s := range compilerArgs {
cmd.AppendArguments(s)
}
cmd.AppendArguments(absPath)
go func() {
compilerMsgB, _ := cmd.Output()
compilerMsg := string(compilerMsgB)
cmd.Args = cmd.Args[0 : len(cmd.Args)-1] //remove the last, which is the file
if strings.Contains(compilerMsg, "error") {
t.log(compilerMsg)
}
}()
}
// t.log("%d Typescript file(s) compiled ( %d monitored by a background file watcher )", len(files), watchedFiles)
}
}
}
}
func (t *Typescript) hasTypescriptFiles() bool {
root := t.Config.Dir
ignoreFolders := strings.Split(t.Config.Ignore, ",")
hasTs := false
if !npm.Exists(root) {
t.log("typescript error: directory '%s' couldn't be found,\nplease specify a valid path for your *.ts files", root)
return false
}
// ignore error
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if fi.IsDir() {
return nil
}
for i := range ignoreFolders {
if strings.Contains(path, ignoreFolders[i]) {
return nil
}
}
if strings.HasSuffix(path, ".ts") {
hasTs = true
return errors.New("Typescript found, hope that will stop here")
}
return nil
})
return hasTs
}
func (t *Typescript) getTypescriptProjects() []string {
var projects []string
ignoreFolders := strings.Split(t.Config.Ignore, ",")
root := t.Config.Dir
//t.logger.Printf("\nSearching for typescript projects in %s", root)
// ignore error
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if fi.IsDir() {
return nil
}
for i := range ignoreFolders {
if strings.Contains(path, ignoreFolders[i]) {
//t.logger.Println(path + " ignored")
return filepath.SkipDir
}
}
if strings.HasSuffix(path, npm.PathSeparator+"tsconfig.json") {
//t.logger.Printf("\nTypescript project found in %s", path)
projects = append(projects, path)
}
return nil
})
return projects
}
// this is being called if getTypescriptProjects return 0 len, then we are searching for files using that:
func (t *Typescript) getTypescriptFiles() []string {
var files []string
ignoreFolders := strings.Split(t.Config.Ignore, ",")
root := t.Config.Dir
// ignore error
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if fi.IsDir() {
return nil
}
for i := range ignoreFolders {
if strings.Contains(path, ignoreFolders[i]) {
//t.logger.Println(path + " ignored")
return nil
}
}
if strings.HasSuffix(path, ".ts") {
//t.logger.Printf("\nTypescript file found in %s", path)
files = append(files, path)
}
return nil
})
return files
}
// Attach attaches the typescript to one or more Iris instance(s).
func (t *Typescript) Attach(app *iris.Application) {
t.log = app.Log
t.start()
}