mirror of
https://github.com/kataras/iris.git
synced 2025-01-24 11:11:03 +01:00
301 lines
8.1 KiB
Go
301 lines
8.1 KiB
Go
|
package typescript
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/kataras/iris"
|
||
|
"github.com/kataras/iris/config"
|
||
|
"github.com/kataras/iris/logger"
|
||
|
"github.com/kataras/iris/npm"
|
||
|
"github.com/kataras/iris/plugin/editor"
|
||
|
"github.com/kataras/iris/utils"
|
||
|
)
|
||
|
|
||
|
/* Notes
|
||
|
|
||
|
The editor is working when the typescript plugin finds a typescript project (tsconfig.json),
|
||
|
also working only if one typescript project found (normaly is one for client-side).
|
||
|
|
||
|
*/
|
||
|
|
||
|
// Name the name of the plugin, is "TypescriptPlugin"
|
||
|
const Name = "TypescriptPlugin"
|
||
|
|
||
|
var nodeModules = utils.PathSeparator + "node_modules" + utils.PathSeparator
|
||
|
|
||
|
type (
|
||
|
// Options the struct which holds the TypescriptPlugin options
|
||
|
// 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
|
||
|
Options struct {
|
||
|
Bin string
|
||
|
Dir string
|
||
|
Ignore string
|
||
|
Tsconfig *Tsconfig
|
||
|
Editor *editor.Plugin // the editor is just a plugin also
|
||
|
}
|
||
|
// Plugin the struct of the Typescript Plugin, holds all necessary fields & methods
|
||
|
Plugin struct {
|
||
|
options Options
|
||
|
// taken from Activate
|
||
|
pluginContainer iris.IPluginContainer
|
||
|
// taken at the PreListen
|
||
|
logger *logger.Logger
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// Editor is just a shortcut for github.com/kataras/iris/plugin/editor.New()
|
||
|
// returns a new (Editor)Plugin, it's exists here because the typescript plugin has direct interest with the EditorPlugin
|
||
|
func Editor(username, password string) *editor.Plugin {
|
||
|
editorCfg := config.DefaultEditor()
|
||
|
editorCfg.Username = username
|
||
|
editorCfg.Password = password
|
||
|
return editor.New(editorCfg)
|
||
|
}
|
||
|
|
||
|
// DefaultOptions returns the default Options of the Plugin
|
||
|
func DefaultOptions() Options {
|
||
|
root, err := os.Getwd()
|
||
|
if err != nil {
|
||
|
panic("Typescript Plugin: Cannot get the Current Working Directory !!! [os.getwd()]")
|
||
|
}
|
||
|
opt := Options{Dir: root + utils.PathSeparator, Ignore: nodeModules, Tsconfig: DefaultTsconfig()}
|
||
|
opt.Bin = npm.Abs("typescript/lib/tsc.js")
|
||
|
return opt
|
||
|
|
||
|
}
|
||
|
|
||
|
// Plugin
|
||
|
|
||
|
// New creates & returns a new instnace typescript plugin
|
||
|
func New(_opt ...Options) *Plugin {
|
||
|
var options = DefaultOptions()
|
||
|
|
||
|
if _opt != nil && len(_opt) > 0 { //not nil always but I like this way :)
|
||
|
opt := _opt[0]
|
||
|
|
||
|
if opt.Bin != "" {
|
||
|
options.Bin = opt.Bin
|
||
|
}
|
||
|
if opt.Dir != "" {
|
||
|
options.Dir = opt.Dir
|
||
|
}
|
||
|
|
||
|
if !strings.Contains(opt.Ignore, nodeModules) {
|
||
|
opt.Ignore += "," + nodeModules
|
||
|
}
|
||
|
|
||
|
if opt.Tsconfig != nil {
|
||
|
options.Tsconfig = opt.Tsconfig
|
||
|
}
|
||
|
|
||
|
options.Ignore = opt.Ignore
|
||
|
}
|
||
|
|
||
|
return &Plugin{options: options}
|
||
|
}
|
||
|
|
||
|
// implement the IPlugin & IPluginPreListen
|
||
|
|
||
|
// Activate ...
|
||
|
func (t *Plugin) Activate(container iris.IPluginContainer) error {
|
||
|
t.pluginContainer = container
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetName ...
|
||
|
func (t *Plugin) GetName() string {
|
||
|
return Name + "[" + utils.RandomString(10) + "]" // this allows the specific plugin to be registed more than one time
|
||
|
}
|
||
|
|
||
|
// GetDescription TypescriptPlugin scans and compile typescript files with ease
|
||
|
func (t *Plugin) GetDescription() string {
|
||
|
return Name + " scans and compile typescript files with ease. \n"
|
||
|
}
|
||
|
|
||
|
// PreListen ...
|
||
|
func (t *Plugin) PreListen(s *iris.Iris) {
|
||
|
t.logger = s.Logger()
|
||
|
t.start()
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
// implementation
|
||
|
|
||
|
func (t *Plugin) start() {
|
||
|
defaultCompilerArgs := t.options.Tsconfig.CompilerArgs() //these will be used if no .tsconfig found.
|
||
|
if t.hasTypescriptFiles() {
|
||
|
//Can't check if permission denied returns always exists = true....
|
||
|
//typescriptModule := out + string(os.PathSeparator) + "typescript" + string(os.PathSeparator) + "bin"
|
||
|
if !npm.Exists(t.options.Bin) {
|
||
|
t.logger.Println("Installing typescript, please wait...")
|
||
|
res := npm.Install("typescript")
|
||
|
if res.Error != nil {
|
||
|
t.logger.Print(res.Error.Error())
|
||
|
return
|
||
|
}
|
||
|
t.logger.Print(res.Message)
|
||
|
|
||
|
}
|
||
|
|
||
|
projects := t.getTypescriptProjects()
|
||
|
if len(projects) > 0 {
|
||
|
watchedProjects := 0
|
||
|
//typescript project (.tsconfig) found
|
||
|
for _, project := range projects {
|
||
|
cmd := utils.CommandBuilder("node", t.options.Bin, "-p", project[0:strings.LastIndex(project, utils.PathSeparator)]) //remove the /tsconfig.json)
|
||
|
projectConfig := FromFile(project)
|
||
|
|
||
|
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.logger.Println(err.Error())
|
||
|
return
|
||
|
}
|
||
|
}()
|
||
|
} else {
|
||
|
|
||
|
_, err := cmd.Output()
|
||
|
if err != nil {
|
||
|
t.logger.Println(err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
t.logger.Printf("%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.options.Tsconfig.CompilerOptions.Watch {
|
||
|
watchedFiles = len(files)
|
||
|
}
|
||
|
//it must be always > 0 if we came here, because of if hasTypescriptFiles == true.
|
||
|
for _, file := range files {
|
||
|
cmd := utils.CommandBuilder("node", t.options.Bin)
|
||
|
cmd.AppendArguments(defaultCompilerArgs...)
|
||
|
cmd.AppendArguments(file)
|
||
|
_, err := cmd.Output()
|
||
|
cmd.Args = cmd.Args[0 : len(cmd.Args)-1] //remove the last, which is the file
|
||
|
if err != nil {
|
||
|
t.logger.Println(err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
}
|
||
|
t.logger.Printf("%d Typescript file(s) compiled ( %d monitored by a background file watcher )", len(files), watchedFiles)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//editor activation
|
||
|
if len(projects) == 1 && t.options.Editor != nil {
|
||
|
dir := projects[0][0:strings.LastIndex(projects[0], utils.PathSeparator)]
|
||
|
t.options.Editor.Dir(dir)
|
||
|
t.pluginContainer.Add(t.options.Editor)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (t *Plugin) hasTypescriptFiles() bool {
|
||
|
root := t.options.Dir
|
||
|
ignoreFolders := strings.Split(t.options.Ignore, ",")
|
||
|
hasTs := false
|
||
|
|
||
|
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 *Plugin) getTypescriptProjects() []string {
|
||
|
var projects []string
|
||
|
ignoreFolders := strings.Split(t.options.Ignore, ",")
|
||
|
|
||
|
root := t.options.Dir
|
||
|
//t.logger.Printf("\nSearching for typescript projects in %s", root)
|
||
|
|
||
|
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, utils.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 *Plugin) getTypescriptFiles() []string {
|
||
|
var files []string
|
||
|
ignoreFolders := strings.Split(t.options.Ignore, ",")
|
||
|
|
||
|
root := t.options.Dir
|
||
|
//t.logger.Printf("\nSearching for typescript files in %s", root)
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
//
|
||
|
//
|