├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── bin ├── scriptor ├── scriptor.es6 ├── scriptor.js └── tools │ ├── cli.es6 │ └── cli.js ├── build ├── defaults.js ├── error.js ├── extensions.js ├── index.js ├── manager.js ├── script.js ├── source_script.js ├── text_script.js └── utils.js ├── docs ├── amd.md ├── api.md ├── cli.md ├── examples │ ├── example 1 │ │ ├── app.js │ │ └── index.js │ ├── example 2 │ │ ├── index.js │ │ └── test.js │ ├── example 3 │ │ ├── index.js │ │ └── something.js │ ├── example 4 │ │ └── index.js │ ├── example 5 │ │ └── index.js │ ├── example 6 │ │ └── index.js │ ├── example 7 │ │ └── index.js │ ├── example 8 │ │ ├── index.js │ │ └── something.js │ └── package.json ├── internal_api.md └── recipes │ ├── async-plugin │ ├── README.md │ ├── index.js │ ├── local-text.js │ └── lorum-ipsum.txt │ ├── custom extension handlers │ ├── README.md │ ├── dot-require-async.js │ ├── dot-require-sync.js │ ├── jsx-require-async.js │ └── jsx-require-sync.js │ └── react-server │ ├── README.md │ ├── app.js │ ├── lib │ ├── dot-require-async.js │ ├── jsx-require-async.js │ ├── react-factory.js │ └── react-render.js │ ├── package.json │ └── views │ ├── components │ └── hello-world.jsx │ ├── index.jsx │ ├── page.jsx │ └── react-top.dot ├── index.js ├── package.json ├── src ├── defaults.js ├── error.js ├── extensions.js ├── index.js ├── manager.js ├── script.js ├── source_script.js ├── text_script.js └── utils.js └── test ├── build ├── 0_basic.js ├── 1_script_init.js ├── 2_script_loading.js ├── 3_script_calling.js ├── 4_script_watching.js ├── 5_amd_scripts.js ├── 6_text_scripts.js ├── 7_amd_errors.js └── 8_manager.js ├── fixtures ├── amd │ ├── builtin_plugins.js │ ├── error.js │ └── simple.js ├── calling │ ├── arguments.js │ ├── coroutine.js │ └── simple.js ├── empty.js ├── loading │ ├── amd_simple.js │ ├── amd_strict.js │ └── commonjs_simple.js ├── text │ └── hello.txt └── watching │ └── simple.js └── src ├── 0_basic.js ├── 1_script_init.js ├── 2_script_loading.js ├── 3_script_calling.js ├── 4_script_watching.js ├── 5_amd_scripts.js ├── 6_text_scripts.js ├── 7_amd_errors.js └── 8_manager.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | 27 | # JetBrains exclusion 28 | .idea 29 | 30 | # TypeScript compiler notes 31 | .tscache 32 | .baseDir.* 33 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | 27 | # JetBrains exclusion 28 | .idea 29 | 30 | # Source files 31 | src 32 | 33 | # example files 34 | examples 35 | 36 | # test files 37 | test 38 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Scriptor Changelog 2 | ================== 3 | 4 | #####3.0.0-alpha.1 5 | * (SEMVER MAJOR) Factory function passed to define will be re-run if an error is thrown. 6 | * This prevents subtle errors where a script is fully loaded, but since the factory function failed the module was not properly exported. 7 | * Normal scripts take explicit precedence over normal scripts. If a script is loading in text mode and it's needed in normal mode, it will load anyway and cancel the other load. 8 | 9 | #####2.6.0 10 | * Refactored all loading and pending promises to use the event system underneath. This should prevent leaky promises. 11 | * In conjunction with the top, the unloading process has been changed somewhat and all loading operations are not allowed when it's already loading. 12 | 13 | #####2.5.5 14 | * Force textMode to false on require. 15 | 16 | #####2.5.4 17 | * Added `--max_listeners ` command line option to set max listeners on the host Manager 18 | 19 | #####2.5.3 20 | * Added `Manager.setMaxListeners` and `Manager.getMaxListeners` to control the maximum number of listeners on new scripts added to the Manager instance. 21 | 22 | #####2.5.2 23 | * Support exports['default'] AMD syntax, commonly used with ES6 modules. 24 | 25 | #####2.5.1 26 | * Prevent potential memory leaks caused by adding and calling many non-existent scripts to a Manager instance. For example, when serving scripts on a server, a DDoS could be possible just trying to access thousands of non-existent scripts, which although resulting in a 404, could create many unused Script instances in the Manager that are never used or cleared. This patch removes Script instances from Managers if an error is thrown while calling them. 27 | 28 | #####2.5.0 29 | Breaking changes: 30 | * Moved from tj/co to bluebird coroutines 31 | * Bluebird coroutines are much less opinionated and allow better stack traces for complex code paths 32 | 33 | #####2.4.0 34 | * Upgraded the pseudo-RequireJS config normalization functions 35 | * Only resolve paths relative to baseUrl when the path is relative (e.g., begins with a dot "./test.js") 36 | 37 | #####2.3.2 38 | * Fixed race condition mentioned in issue #6 39 | * Added `.loading` value to scripts 40 | 41 | #####2.3.1 42 | * Added `.dependencies` to view script dependencies specified via `define` 43 | 44 | #####2.3.0 45 | * New TextScript class 46 | * New `textMode` property on Scripts 47 | * New `text!` plugin for TextScripts 48 | * Buffers now used for source storage 49 | * File watchers debounce changes. Debounce time can be set (before watching) via `debounceMaxWait` property 50 | * Event propagation base improvements 51 | * Limited support for RequireJS-like `.config` configuration settings with `paths` value for module lookup 52 | * Optionally disable Scriptor from setting process title 53 | 54 | #####2.1.0 55 | * Added `.source()` on normal scripts, which custom extensions can populate by returning the source string 56 | 57 | #####2.0.1 58 | * Removed spacing in the AMD Header that would cause some line number issues 59 | 60 | ##Version 2.0 61 | * Completely asynchronous build 62 | * Many Reference types with advanced features 63 | * Standalone executable 64 | * Full AMD support natively 65 | * Much better documentation and examples 66 | * In general much, much better quality 67 | 68 | ##Pre-2.0 69 | 70 | #####1.4.0-alpha.4 71 | * Added in real async loading for situations that allows it 72 | * Implemented more of the AMD spec 73 | * Fixed and optimized a few things 74 | * Still working on documentation 75 | * Added async dependency for async.map 76 | 77 | #####1.4.0-alpha.3 78 | * added `clearReference` function 79 | * Bettered inheritance model 80 | * Working on documentation now 81 | 82 | #####1.4.0-alpha.2 83 | * Removed fake async require 84 | * Added `require.specified`, `require.defined` and `onload.error` AMD functions 85 | * Fixed bug in plugin loading 86 | 87 | #####1.4.0-alpha.1 88 | * use `url.resolve` instead of `path.resolve` in `requite.toUrl` 89 | * assert AMD plugin is not null 90 | 91 | #####1.4.0-alpha.0 92 | * Big changes, though not breaking changes 93 | * Removed amdefine dependency 94 | * Implemented amdefine functionality into Scriptor natively 95 | * More AMD features taking advantage of Scriptor systems 96 | * Use of Map data structure where available, with standard object fallback. 97 | * Have not documented changes yet, but that is in progress. 98 | 99 | #####1.3.9 100 | * Simplified a function call 101 | 102 | #####1.3.8 103 | * Upped protection of loading and executing scripts 104 | * Script.exports now evaluates the script if it has not already done so, thereby allowing direct use of .exports 105 | * Added `cwd` and `chdir` property and function to the script Manager class so scripts can have specific locations instead of relative to the manager. 106 | 107 | #####1.3.7 108 | * Fixed issue where the recursion protection would mess up and give false positives. 109 | 110 | #####1.3.6 111 | * Reverted last version changes because the issue was unrelated to the Scriptor library, but instead with dual module dependencies behaving weird. 112 | 113 | #####1.3.5 114 | * Fixed issue with instanceof not handling inherited classes 115 | 116 | #####1.3.4 117 | * Added `.change` function to script environment so it can trigger Reference resets 118 | 119 | #####1.3.3 120 | * Renamed all Referee stuff into Reference. Referee was such a horrible name in retrospect. 121 | 122 | #####1.3.2 123 | * Added some type assertions 124 | * Fixed bug in script where References were not being stored 125 | * `.load` now emits 'change' events 126 | * Strip BOM on SourceScript if needed 127 | * Fixed bug where join and transform functions might not have worked chained together 128 | * Renamed Manager.once_apply to apply_once, and added call_once to manager. 129 | * Prepping to move Referee stuff over to a new transform library, as it's a bit too much to have in a scripting library. 130 | * Realized Referee is not a good name for those. "I are good namer." 131 | 132 | #####1.3.1 133 | * Updated package.json description. 134 | 135 | #####1.3.0 136 | * Added [`SourceScript`](#sourcescript) 137 | * Added [`.transform`](#transformtransform--itransformfunction---reference) method 138 | * Added documentation for [`ITransformFunction`](#itransformfunction) 139 | * Reworked some internals and changed some defaults 140 | * Added [`.call_once`](#call_onceargs--any---reference) and [`.apply_once`](#apply_onceargs--any---reference) methods 141 | * Made [`.reload`](#reload---boolean) invalidate References 142 | * Added [`Scriptor.load`](#scriptorloadfilename--string-watch--boolean---script) and [`Scriptor.compile`](#scriptorcompilesrc--stringreference-watch--boolean---sourcescript) methods 143 | * Prevented multiple closing on References 144 | * Updated left/right join semantics 145 | 146 | #####1.2.4 147 | * Added [`.close()`](#closerecursive--boolean) methods and [`.closed`](#closed---boolean) properties to References 148 | * Fixed bug in [`.load()`](#loadfilename--string-watch--boolean---script) that prevented watching a new filename 149 | * Removed all non-strict equalities and inequalities 150 | * Use `void 0` where possible instead of null 151 | * Overprotected file watcher system in case of potential file deletion events 152 | * Was unable to test it, though. 153 | * Made forced reloading in [`.include()`](#includefilename--string-load--boolean---script) optional 154 | 155 | #####1.2.3 156 | * Forgot to freeze another part of Reference values 157 | 158 | #####1.2.2 159 | * Freeze Reference values to prevent accidental tampering by destructive functions 160 | 161 | #####1.2.1 162 | * Fixed a few things JSHint complained about. 163 | 164 | #####1.2.0 165 | * Added [`Reference.join_all`](#referencejoin_allrefs--reference-transform--function---reference) function 166 | * Fixed typo 167 | 168 | #####1.1.1 169 | * Modified internal semantics for [`.join`](#joinref--reference-transform--function---reference) 170 | * Added static [`Reference.join()`](#referencejoinleft--reference-right--reference-transform--function---reference) function 171 | * Added [`.left()`](#left---reference) and [`.right()`](#right---reference) 172 | * Fixed a few typos 173 | 174 | #####1.1.0 175 | * [`.join`](#joinref--reference-transform--function---reference) function on References 176 | * [`.imports`](#imports---any) and [`.exports`](#exports---any) docs 177 | * [`.maxRecursion`](#maxrecursion---number) and recursion protection 178 | 179 | #####1.0.2 180 | * README fixes 181 | 182 | #####1.0.1 183 | * Fixed a few typos in README.md 184 | * Added npm badges 185 | 186 | #####1.0.0 187 | 188 | * First full release 189 | * API improvements 190 | * Full documentation 191 | * A couple bugfixes 192 | * Reference wasn't actually marking the result as ran (fixed) 193 | 194 | #####pre-1.0.0 195 | * development 196 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 7/8/14. 3 | */ 4 | 5 | module.exports = function( grunt ) { 6 | 7 | grunt.loadNpmTasks( 'grunt-babel' ); 8 | grunt.loadNpmTasks( 'grunt-contrib-clean' ); 9 | grunt.loadNpmTasks( 'grunt-banner' ); 10 | 11 | var LICENSE = '/****\n * ' + grunt.file.read( './LICENSE', {encoding: 'utf-8'} ).replace( /\n/ig, '\n * ' ) 12 | + '\n ****/'; 13 | 14 | var loose = {loose: true}; 15 | 16 | grunt.initConfig( { 17 | babel: { 18 | options: { 19 | ast: false, 20 | sourceMaps: false, 21 | compact: false 22 | }, 23 | build: { 24 | options: { 25 | plugins: [ 26 | [ 27 | "transform-async-to-module-method", 28 | {"module": "bluebird", "method": "coroutine", loose: true} 29 | ], 30 | ["transform-class-constructor-call", loose], 31 | ["transform-class-properties", loose], 32 | ["transform-decorators", loose], 33 | ["transform-do-expressions", loose], 34 | ["transform-strict-mode", loose], 35 | ["transform-es2015-arrow-functions", loose], 36 | ["transform-es2015-block-scoped-functions", loose], 37 | ["transform-es2015-block-scoping", loose], 38 | ["transform-es2015-classes", loose], 39 | ["transform-es2015-computed-properties", loose], 40 | ["transform-es2015-destructuring", loose], 41 | ["transform-es2015-for-of", loose], 42 | ["transform-es2015-function-name", loose], 43 | ["transform-es2015-literals", loose], 44 | ["transform-es2015-modules-commonjs", loose], 45 | ["transform-es2015-object-super", loose], 46 | ["transform-es2015-parameters", loose], 47 | ["transform-es2015-shorthand-properties", loose], 48 | ["transform-es2015-spread", loose], 49 | ["transform-es2015-sticky-regex", loose], 50 | ["transform-es2015-template-literals", loose], 51 | ["transform-es2015-typeof-symbol", loose], 52 | ["transform-es2015-unicode-regex", loose], 53 | ["transform-exponentiation-operator", loose], 54 | ["transform-export-extensions", loose], 55 | ["transform-function-bind", loose], 56 | ["transform-object-rest-spread", loose], 57 | ["transform-undefined-to-void", loose], 58 | ["transform-runtime", loose] 59 | ] 60 | }, 61 | files: [ 62 | { 63 | expand: true, 64 | cwd: './src/', 65 | src: './**/*.js', 66 | dest: './build/' 67 | }, 68 | { 69 | expand: true, 70 | cwd: './test/src/', 71 | src: './**/*.js', 72 | dest: './test/build/' 73 | }, 74 | { 75 | expand: true, 76 | cwd: './bin/', 77 | src: './**/*.es6', 78 | dest: './bin/', 79 | ext: '.js' 80 | } 81 | ] 82 | } 83 | }, 84 | usebanner: { 85 | license: { 86 | options: { 87 | position: 'top', 88 | banner: LICENSE, 89 | linebreak: true 90 | }, 91 | files: { 92 | src: ['./build/**/*.js', './bin/**/*.js'] 93 | } 94 | } 95 | }, 96 | clean: { 97 | build: { 98 | src: ['./build', './bin/**/*.js', './bin/**/*.map'] 99 | }, 100 | tests: { 101 | src: ['./test/build'] 102 | } 103 | } 104 | } ); 105 | 106 | grunt.registerTask( 'build', [ 107 | 'clean:tests', 108 | 'clean:build', 109 | 'babel:build', 110 | 'usebanner:license' 111 | ] ); 112 | 113 | grunt.registerTask( 'default', ['build'] ); 114 | }; 115 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Aaron Trent 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Scriptor 2 | ======== 3 | 4 | ### This project is defunct and outdated. I would not recommend using it, but feel free to browse the code and steal anything useful. 5 | 6 | [![NPM Version][npm-image]][npm-url] 7 | [![NPM Downloads][downloads-image]][npm-url] 8 | [![Codacy Score][codacy-image]][codacy-url] 9 | [![MIT License][license-image]][npm-url] 10 | 11 | # Introduction 12 | Scriptor is the ultimate library for dynamically loading, reloading and running scripts, with built-in support for file watching to automatically reload when necessary. 13 | 14 | A script is defined as a file which can be 'required' via CommonJS `require` function in node and io.js. It can be a `.js`, `.json`, and even `.node` or any other installed extension. For the asynchronous build of Scriptor, custom extensions can even be defined that take advantage of Promises and Coroutines. 15 | 16 | Additionally, Scriptor includes a Manager class that helps to coordinate many scripts and even allow them to cross-reference each other to form more complex multi-script applications. 17 | 18 | As of Version 2.0, Scriptor now has a command line interface capable of running Scriptor AMD scripts either synchronously or asynchronously. It can effectively replace `node` in the case of AMD scripts. By default, it uses the custom extension handler to inject the define function and load files. 19 | 20 | # Purpose 21 | The initial purpose of Scriptor was to create a system for writing simple scripts that generate web pages. It has since become a standalone library. 22 | 23 | # Topics 24 | * [Quick Start](#quick-start) 25 | * [Features](#scriptor-features) 26 | * [Documentation](#documentation) 27 | * [API Documentation](/docs/api.md) 28 | * [Asynchronous Module Definitions](/docs/amd.md) 29 | * [CLI Documentation](/docs/cli.md) 30 | * [Example Code](#example-code-and-useful-recipes) 31 | * [Support](#support) 32 | * [Changelog](/CHANGELOG.md) 33 | * [License](#license) 34 | 35 | # Quick Start 36 | `npm install scriptor` or `npm install -g scriptor` 37 | 38 | Then: 39 | 40 | `var Scriptor = require('scriptor');` 41 | 42 | Or for the command line interface: 43 | 44 | ``` 45 | $scriptor -h 46 | 47 | Usage: scriptor [options] files... 48 | 49 | Options: 50 | 51 | -h, --help output usage information 52 | -V, --version output the version number 53 | -d, --dir Directory to run Scriptor in 54 | -a, --async Run scripts asynchronously 55 | -c, --concurrency Limit script concurrency to n when executed asynchronously (default: max_recursion + 1) 56 | -q, --close End the process when all scripts finish 57 | -w, --watch Watch scripts for changes and re-run them when changed 58 | -p, --propagate Propagate changes upwards when watching scripts 59 | -l, --long_stack_traces Display long stack trace for asynchronous errors 60 | -r, --repeat Run script n times (in parallel if async) 61 | -u, --unique Only run unique scripts (will ignore duplicates in file arguments) 62 | --debounce Wait n milliseconds for debounce on file watching events (default: 50ms) 63 | --use_strict Enforce strict mode 64 | --max_listeners Set the maximum number of listeners on any particular script 65 | --max_recursion Set the maximum recursion depth of scripts (default: 9) 66 | -v, --verbose [n] Print out extra status information (0 - normal, 1 - info, 2 - verbose) 67 | --cork Cork stdout before calling scripts 68 | -s, --silent Do not echo anything 69 | --no_ext Disable use of custom extensions with AMD injection 70 | --no_signal Do not intercept process signals 71 | --no_glob Do not match glob patterns 72 | --no_title Do not set process title 73 | ``` 74 | 75 | # Scriptor Features 76 | 77 | ##### Synchronous or Asynchronous 78 | Scriptor comes in two variations. There is the normal, synchronous build, which lazily evaluates scripts, but returns values synchronously. 79 | 80 | Then there is the asynchronous build, which not only lazily evaluates scripts, but can load scripts and wait on return values asynchronously using Promises and even run module factories using coroutines. 81 | 82 | For the advantages or disadvantages of either, see [Here](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#foreword-about-the-sync-and-async-builds) 83 | 84 | ##### AMD 85 | Inside a script, Scriptor injects a few extra functions into the global `module` variable that facilitates Asynchronous Module Definitions (AMD). It is a fully fledged AMD implementation complete with plugin support, lazy evaluation of defined modules, and (in the asynchronous build), fully asynchronous dependency resolution. All of which integrates gracefully with standard Node.js modules and Scriptor itself. 86 | 87 | For a full tutorial on how to best use Scriptor with AMD, see [Here](/docs/amd.md) 88 | 89 | ##### Automatic Reloading 90 | Scriptor utilizes file watchers to automatically reload and rename scripts as needed. No need to manually recompile scripts using complex file watcher code. It's all built-in. 91 | 92 | ##### References 93 | Scriptor has a way overpowered system that can be used to 'reference' a single evaluation of a script and automatically reload and re-evaluate the reference when the script changes. In addition, Scriptor includes a fully fledged system for merging, transforming and generally manipulating references values while still propagating changes in the originating scripts. 94 | 95 | For more information on this feature, see [Here](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#reference) 96 | 97 | # Documentation 98 | 99 | * [API Documentation](/docs/api.md) 100 | * [Asynchronous Module Definitions](/docs/amd.md) 101 | * [CLI Documentation](/docs/cli.md) 102 | 103 | # Example Code and Useful Recipes 104 | * [Example Custom Extension Handlers](https://github.com/novacrazy/scriptor/tree/master/docs/recipes/custom%20extension%20handlers) 105 | * [Example Server using React and Koa](https://github.com/novacrazy/scriptor/tree/master/docs/recipes/react-server) 106 | * [Example AMD Plugin](https://github.com/novacrazy/scriptor/tree/master/docs/recipes/async-plugin) 107 | 108 | # Support 109 | * [Github issues for bugs and feature requests](/issues) 110 | * Email me at [novacrazy@gmail.com](mailto://novacrazy@gmail.com) for help and advice. 111 | 112 | # License 113 | The MIT License (MIT) 114 | 115 | Copyright (c) 2015-2016 Aaron Trent 116 | 117 | Permission is hereby granted, free of charge, to any person obtaining a copy 118 | of this software and associated documentation files (the "Software"), to deal 119 | in the Software without restriction, including without limitation the rights 120 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 121 | copies of the Software, and to permit persons to whom the Software is 122 | furnished to do so, subject to the following conditions: 123 | 124 | The above copyright notice and this permission notice shall be included in all 125 | copies or substantial portions of the Software. 126 | 127 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 128 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 129 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 130 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 131 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 132 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 133 | SOFTWARE. 134 | 135 | [npm-image]: https://img.shields.io/npm/v/scriptor.svg?style=flat 136 | [npm-url]: https://npmjs.org/package/scriptor 137 | [downloads-image]: https://img.shields.io/npm/dm/scriptor.svg?style=flat 138 | [codacy-image]: https://img.shields.io/codacy/2143c559823843aa9a25ade263aff0e3.svg?style=flat 139 | [codacy-url]: https://www.codacy.com/public/novacrazy/scriptor 140 | [license-image]: https://img.shields.io/npm/l/scriptor.svg?style=flat 141 | -------------------------------------------------------------------------------- /bin/scriptor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require( './scriptor.js' ).default( process.argv ); 4 | -------------------------------------------------------------------------------- /bin/scriptor.es6: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 6/30/2015. 3 | */ 4 | 5 | import options from 'commander'; 6 | import Module from 'module'; 7 | import glob from 'glob'; 8 | import path from 'path'; 9 | import package_json from './../package.json'; 10 | 11 | import _ from 'lodash'; 12 | 13 | let constants = process.binding( 'constants' ); 14 | 15 | import {default_debounceMaxWait} from './../build/modern/defaults.js'; 16 | import {Logger, LogLevel} from './tools/cli.js'; 17 | 18 | import Scriptor from './../build/modern/index.js'; 19 | 20 | function diff_ms( start ) { 21 | let [seconds, nanoseconds] = process.hrtime( start ); 22 | 23 | let milliseconds = seconds * 1e3 + nanoseconds * 1e-6; 24 | 25 | return milliseconds.toFixed( 3 ) + 'ms'; 26 | } 27 | 28 | let ms_pattern = /([0-9]+)([A-Z]+)?/i; 29 | 30 | function toMilliseconds( str ) { 31 | let match = str.match( ms_pattern ); 32 | 33 | if( match ) { 34 | let i = parseInt( match[1] ); 35 | 36 | if( !isNaN( i ) ) { 37 | let u = match[2]; 38 | 39 | if( typeof u === 'string' ) { 40 | u = u.toLowerCase(); 41 | 42 | } else if( u === void 0 ) { 43 | return i; 44 | } 45 | 46 | switch( u ) { 47 | case 'ms': 48 | return i; 49 | case 's': 50 | return i * 1000; 51 | case 'm': 52 | return i * 60 * 1000; 53 | case 'h': 54 | return i * 60 * 60 * 1000; 55 | case 'd': 56 | return i * 24 * 60 * 60 * 1000; 57 | case 'y': 58 | return i * 365 * 24 * 60 * 60 * 1000; 59 | default: 60 | return i; 61 | } 62 | } 63 | } 64 | 65 | return null; 66 | } 67 | 68 | function parseIntOrInfinity( value ) { 69 | if( value.toLowerCase() === 'infinity' ) { 70 | return Infinity; 71 | 72 | } else { 73 | return parseInt( value ); 74 | } 75 | } 76 | 77 | //Process status codes 78 | let EXIT_SUCCESS = 0, 79 | EXIT_FAILURE = 1; 80 | 81 | options 82 | .version( package_json.version ) 83 | .usage( '[options] files...' ) 84 | .option( '-d, --dir ', 'Directory to run Scriptor in' ) 85 | .option( '-c, --concurrency ', 'Limit asynchronous script concurrency to n (default: 10)' ) 86 | .option( '-q, --close', 'End the process when all scripts finish' ) 87 | .option( '-w, --watch', 'Watch scripts for changes and re-run them when changed' ) 88 | .option( '-p, --propagate', 'Propagate changes upwards when watching scripts' ) 89 | .option( '-l, --long_stack_traces', 'Display long stack trace for errors' ) 90 | .option( '-r, --repeat ', 'Run script n times (in parallel if possible)' ) 91 | .option( '-u, --unique', 'Only run unique scripts (will ignore duplicates in file arguments)' ) 92 | .option( '--debounce ', 93 | `Wait n milliseconds for debounce on file watching events (default: ${default_debounceMaxWait}ms)` ) 94 | .option( '--use_strict', 'Enforce strict mode' ) 95 | .option( '--max_listeners ', 'Set the maximum number of listeners on any particular script' ) 96 | .option( '-v, --verbose [n]', 'Print out extra status information (0 - normal, 1 - info, 2 - verbose)' ) 97 | .option( '--cork', 'Cork stdout before calling scripts' ) 98 | .option( '-s, --silent', 'Do not echo anything' ) 99 | .option( '--no_signal', 'Do not intercept process signals' ) 100 | .option( '--no_glob', 'Do not match glob patterns' ) 101 | .option( '--no_title', 'Do not set process title' ); 102 | 103 | export default function( argv ) { 104 | process.options = options.parse( argv ); 105 | 106 | if( options.use_strict ) { 107 | Module.wrapper[0] += '"use strict";'; 108 | Object.freeze( Module.wrap ); 109 | } 110 | 111 | if( !options.no_title ) { 112 | process.title = 'Scriptor'; 113 | } 114 | 115 | //The default log_level is LOG_NORMAL 116 | let log_level = LogLevel.LOG_NORMAL; 117 | 118 | //If silent mode is enabled, it overrides verbose mode 119 | if( options.silent ) { 120 | log_level = LogLevel.LOG_SILENT; 121 | 122 | } else if( options.verbose ) { 123 | //If using the -v shorthand, it is essentially --verbose 2 124 | if( typeof options.verbose === 'boolean' ) { 125 | log_level = LogLevel.LOG_VERBOSE; 126 | 127 | } else { 128 | log_level = parseInt( options.verbose ); 129 | 130 | //Instead of throwing an error, just use the normal level 131 | if( isNaN( log_level ) ) { 132 | log_level = LogLevel.LOG_NORMAL; 133 | } 134 | } 135 | } 136 | 137 | //Create the logger 138 | let logger = new Logger( log_level ); 139 | 140 | //Unhandled errors are printed and the process is killed 141 | let onError = error => { 142 | logger.error( error.stack || error ); 143 | process.exit( EXIT_FAILURE ); 144 | }; 145 | 146 | process.on( 'uncaughtException', onError ); 147 | process.on( 'unhandledRejection', onError ); 148 | 149 | let scripts; 150 | 151 | if( !options.no_glob ) { 152 | scripts = []; 153 | 154 | for( let arg of options.args ) { 155 | if( glob.hasMagic( arg ) ) { 156 | scripts = scripts.concat( glob.sync( arg ) ); 157 | 158 | } else { 159 | scripts.push( arg ); 160 | } 161 | } 162 | 163 | } else { 164 | scripts = options.args; 165 | } 166 | 167 | if( options.unique ) { 168 | logger.info( 'Only executing unique scripts' ); 169 | 170 | let uniqueScripts = []; 171 | 172 | scripts.forEach( script => { 173 | if( uniqueScripts.indexOf( script ) === -1 ) { 174 | uniqueScripts.push( script ); 175 | } 176 | } ); 177 | 178 | scripts = uniqueScripts; 179 | } 180 | 181 | if( options.repeat ) { 182 | let count = parseInt( options.repeat ); 183 | 184 | if( !isNaN( count ) && count > 1 ) { 185 | logger.info( `Repeating script execution ${count} times` ); 186 | 187 | let newScripts = []; 188 | 189 | for( let i = 0; i < count; i++ ) { 190 | newScripts = newScripts.concat( scripts ); 191 | } 192 | 193 | scripts = newScripts; 194 | } 195 | } 196 | 197 | /* 198 | * options.dir only says the directory scriptor will be run in, 199 | * not the directory where it looks for files. So it will resolve files relative to the 200 | * invocation location, but change process.cwd to the new location. 201 | * Since the script files will be absolute now, now issues should arrive when using the manager. 202 | * */ 203 | if( typeof options.dir === 'string' ) { 204 | scripts = scripts.map( script => { 205 | return path.resolve( process.cwd(), script ); 206 | } ); 207 | 208 | process.chdir( options.dir ); 209 | } 210 | 211 | //Only run anything if there are any scripts to run, otherwise show the help 212 | if( scripts.length > 0 ) { 213 | if( options.long_stack_traces ) { 214 | Scriptor.Promise.longStackTraces(); 215 | logger.info( 'Long Stack Traces enabled' ); 216 | } 217 | 218 | //New manager created, using process.cwd as the cwd 219 | let manager = new Scriptor.Manager(); 220 | 221 | if( options.propagate ) { 222 | manager.propagateEvents(); 223 | } 224 | 225 | let concurrency = 10, watch, debounce; 226 | 227 | if( typeof options.debounce === 'string' ) { 228 | debounce = manager.debounceMaxWait = toMilliseconds( options.debounce ); 229 | } 230 | 231 | if( typeof options.max_listeners === 'string' ) { 232 | let maxListeners = parseInt( options.max_listeners ); 233 | 234 | if( isFinite( maxListeners ) ) { 235 | manager.setMaxListeners( maxListeners ); 236 | 237 | } else { 238 | logger.error( 'Not a finite Number value for option --max_listeners' ); 239 | process.exit( EXIT_FAILURE ); 240 | } 241 | } 242 | 243 | 244 | if( options.concurrency ) { 245 | concurrency = parseIntOrInfinity( options.concurrency ); 246 | 247 | if( isNaN( concurrency ) ) { 248 | logger.error( 'Not a Number value for option -c, --concurrency' ); 249 | process.exit( EXIT_FAILURE ); 250 | } 251 | } 252 | 253 | //Close overrides watch, so if it is set to close, don't even both watching the scripts 254 | if( options.watch && !options.close ) { 255 | watch = true; 256 | } 257 | 258 | let run_script, 259 | script_start = process.hrtime(), 260 | place = 0, 261 | instances = []; 262 | 263 | if( !options.no_signal ) { 264 | let closed = false; 265 | 266 | let onClose = signal => { 267 | if( !closed ) { 268 | 269 | for( let instance of instances ) { 270 | instance.unload(); 271 | } 272 | 273 | //Close on the next tick so close events can propagate. 274 | //Exit code for Ctrl-C signals is 128 + sig according to 275 | // http://www.tldp.org/LDP/abs/html/exitcodes.html 276 | process.nextTick( process.exit.bind( null, 128 + signal ) ); 277 | 278 | closed = true; 279 | } 280 | }; 281 | 282 | process.on( 'SIGINT', onClose.bind( null, constants.SIGINT ) ); 283 | process.on( 'SIGTERM', onClose.bind( null, constants.SIGTERM ) ); 284 | } 285 | 286 | run_script = ( instance, script, num ) => { 287 | let start = process.hrtime(); 288 | 289 | if( log_level === LogLevel.LOG_SILENT || options.cork ) { 290 | process.stdout.cork(); 291 | } 292 | 293 | return instance.call().then( () => { 294 | logger.verbose( 'Finished script #%d, %s in %s.', num, script, diff_ms( start ) ); 295 | } ); 296 | }; 297 | 298 | logger.info( 'Concurrency set at %s', concurrency ); 299 | 300 | Scriptor.Promise.map( scripts, script => { 301 | let num = place++; 302 | 303 | logger.verbose( 'Running script #%d, %s.', num, script ); 304 | 305 | let instance = manager.add( script, false ); 306 | 307 | instances.push( instance ); 308 | 309 | if( watch ) { 310 | instance.watch( true ); 311 | 312 | let onChange = () => { 313 | logger.verbose( 'Re-running script #%d, %s', num, script ); 314 | run_script( instance, script, num ); 315 | }; 316 | 317 | if( typeof debounce === 'number' ) { 318 | onChange = _.debounce( onChange, debounce ); 319 | } 320 | 321 | instance.on( 'change', onChange ); 322 | } 323 | 324 | return run_script( instance, script, num ); 325 | }, { 326 | concurrency: concurrency 327 | 328 | } ).catch( onError ).then( () => { 329 | logger.log( 'All scripts successfully executed in %s', diff_ms( script_start ) ); 330 | 331 | if( options.close ) { 332 | process.exit( EXIT_SUCCESS ); 333 | } 334 | } ); 335 | 336 | } else { 337 | options.help(); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /bin/scriptor.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | 'use strict'; 26 | 27 | exports.__esModule = true; 28 | 29 | var _getIterator2 = require( 'babel-runtime/core-js/get-iterator' ); 30 | 31 | var _getIterator3 = _interopRequireDefault( _getIterator2 ); 32 | 33 | var _freeze = require( 'babel-runtime/core-js/object/freeze' ); 34 | 35 | var _freeze2 = _interopRequireDefault( _freeze ); 36 | 37 | exports.default = function( argv ) { 38 | process.options = _commander2.default.parse( argv ); 39 | 40 | if( _commander2.default.use_strict ) { 41 | _module2.default.wrapper[0] += '"use strict";'; 42 | (0, _freeze2.default)( _module2.default.wrap ); 43 | } 44 | 45 | if( !_commander2.default.no_title ) { 46 | process.title = 'Scriptor'; 47 | } 48 | 49 | //The default log_level is LOG_NORMAL 50 | var log_level = _cli.LogLevel.LOG_NORMAL; 51 | 52 | //If silent mode is enabled, it overrides verbose mode 53 | if( _commander2.default.silent ) { 54 | log_level = _cli.LogLevel.LOG_SILENT; 55 | } else if( _commander2.default.verbose ) { 56 | //If using the -v shorthand, it is essentially --verbose 2 57 | if( typeof _commander2.default.verbose === 'boolean' ) { 58 | log_level = _cli.LogLevel.LOG_VERBOSE; 59 | } else { 60 | log_level = parseInt( _commander2.default.verbose ); 61 | 62 | //Instead of throwing an error, just use the normal level 63 | if( isNaN( log_level ) ) { 64 | log_level = _cli.LogLevel.LOG_NORMAL; 65 | } 66 | } 67 | } 68 | 69 | //Create the logger 70 | var logger = new _cli.Logger( log_level ); 71 | 72 | //Unhandled errors are printed and the process is killed 73 | var onError = function onError( error ) { 74 | logger.error( error.stack || error ); 75 | process.exit( EXIT_FAILURE ); 76 | }; 77 | 78 | process.on( 'uncaughtException', onError ); 79 | process.on( 'unhandledRejection', onError ); 80 | 81 | var scripts = void 0; 82 | 83 | if( !_commander2.default.no_glob ) { 84 | scripts = []; 85 | 86 | for( var _iterator = _commander2.default.args, _isArray = Array.isArray( _iterator ), _i = 0, _iterator = _isArray ? _iterator : 87 | (0, _getIterator3.default)( 88 | _iterator ); ; ) { 89 | var _ref; 90 | 91 | if( _isArray ) { 92 | if( _i >= _iterator.length ) { 93 | break; 94 | } 95 | _ref = _iterator[_i++]; 96 | } else { 97 | _i = _iterator.next(); 98 | if( _i.done ) { 99 | break; 100 | } 101 | _ref = _i.value; 102 | } 103 | 104 | var arg = _ref; 105 | 106 | if( _glob2.default.hasMagic( arg ) ) { 107 | scripts = scripts.concat( _glob2.default.sync( arg ) ); 108 | } else { 109 | scripts.push( arg ); 110 | } 111 | } 112 | } else { 113 | scripts = _commander2.default.args; 114 | } 115 | 116 | if( _commander2.default.unique ) { 117 | (function() { 118 | logger.info( 'Only executing unique scripts' ); 119 | 120 | var uniqueScripts = []; 121 | 122 | scripts.forEach( function( script ) { 123 | if( uniqueScripts.indexOf( script ) === -1 ) { 124 | uniqueScripts.push( script ); 125 | } 126 | } ); 127 | 128 | scripts = uniqueScripts; 129 | })(); 130 | } 131 | 132 | if( _commander2.default.repeat ) { 133 | var count = parseInt( _commander2.default.repeat ); 134 | 135 | if( !isNaN( count ) && count > 1 ) { 136 | logger.info( 'Repeating script execution ' + count + ' times' ); 137 | 138 | var newScripts = []; 139 | 140 | for( var i = 0; i < count; i++ ) { 141 | newScripts = newScripts.concat( scripts ); 142 | } 143 | 144 | scripts = newScripts; 145 | } 146 | } 147 | 148 | /* 149 | * options.dir only says the directory scriptor will be run in, 150 | * not the directory where it looks for files. So it will resolve files relative to the 151 | * invocation location, but change process.cwd to the new location. 152 | * Since the script files will be absolute now, now issues should arrive when using the manager. 153 | * */ 154 | if( typeof _commander2.default.dir === 'string' ) { 155 | scripts = scripts.map( function( script ) { 156 | return _path2.default.resolve( process.cwd(), script ); 157 | } ); 158 | 159 | process.chdir( _commander2.default.dir ); 160 | } 161 | 162 | //Only run anything if there are any scripts to run, otherwise show the help 163 | if( scripts.length > 0 ) { 164 | (function() { 165 | if( _commander2.default.long_stack_traces ) { 166 | _index2.default.Promise.longStackTraces(); 167 | logger.info( 'Long Stack Traces enabled' ); 168 | } 169 | 170 | //New manager created, using process.cwd as the cwd 171 | var manager = new _index2.default.Manager(); 172 | 173 | if( _commander2.default.propagate ) { 174 | manager.propagateEvents(); 175 | } 176 | 177 | var concurrency = 10, 178 | watch = void 0, 179 | debounce = void 0; 180 | 181 | if( typeof _commander2.default.debounce === 'string' ) { 182 | debounce = manager.debounceMaxWait = toMilliseconds( _commander2.default.debounce ); 183 | } 184 | 185 | if( typeof _commander2.default.max_listeners === 'string' ) { 186 | var maxListeners = parseInt( _commander2.default.max_listeners ); 187 | 188 | if( isFinite( maxListeners ) ) { 189 | manager.setMaxListeners( maxListeners ); 190 | } else { 191 | logger.error( 'Not a finite Number value for option --max_listeners' ); 192 | process.exit( EXIT_FAILURE ); 193 | } 194 | } 195 | 196 | if( _commander2.default.concurrency ) { 197 | concurrency = parseIntOrInfinity( _commander2.default.concurrency ); 198 | 199 | if( isNaN( concurrency ) ) { 200 | logger.error( 'Not a Number value for option -c, --concurrency' ); 201 | process.exit( EXIT_FAILURE ); 202 | } 203 | } 204 | 205 | //Close overrides watch, so if it is set to close, don't even both watching the scripts 206 | if( _commander2.default.watch && !_commander2.default.close ) { 207 | watch = true; 208 | } 209 | 210 | var run_script = void 0, 211 | script_start = process.hrtime(), 212 | place = 0, 213 | instances = []; 214 | 215 | if( !_commander2.default.no_signal ) { 216 | (function() { 217 | var closed = false; 218 | 219 | var onClose = function onClose( signal ) { 220 | if( !closed ) { 221 | 222 | for( var _iterator2 = instances, _isArray2 = Array.isArray( _iterator2 ), _i2 = 0, _iterator2 = _isArray2 ? 223 | _iterator2 : 224 | (0, _getIterator3.default)( 225 | _iterator2 ); ; ) { 226 | var _ref2; 227 | 228 | if( _isArray2 ) { 229 | if( _i2 >= _iterator2.length ) { 230 | break; 231 | } 232 | _ref2 = _iterator2[_i2++]; 233 | } else { 234 | _i2 = _iterator2.next(); 235 | if( _i2.done ) { 236 | break; 237 | } 238 | _ref2 = _i2.value; 239 | } 240 | 241 | var instance = _ref2; 242 | 243 | instance.unload(); 244 | } 245 | 246 | //Close on the next tick so close events can propagate. 247 | //Exit code for Ctrl-C signals is 128 + sig according to 248 | // http://www.tldp.org/LDP/abs/html/exitcodes.html 249 | process.nextTick( process.exit.bind( null, 128 + signal ) ); 250 | 251 | closed = true; 252 | } 253 | }; 254 | 255 | process.on( 'SIGINT', onClose.bind( null, constants.SIGINT ) ); 256 | process.on( 'SIGTERM', onClose.bind( null, constants.SIGTERM ) ); 257 | })(); 258 | } 259 | 260 | run_script = function run_script( instance, script, num ) { 261 | var start = process.hrtime(); 262 | 263 | if( log_level === _cli.LogLevel.LOG_SILENT || _commander2.default.cork ) { 264 | process.stdout.cork(); 265 | } 266 | 267 | return instance.call().then( function() { 268 | logger.verbose( 'Finished script #%d, %s in %s.', num, script, diff_ms( start ) ); 269 | } ); 270 | }; 271 | 272 | logger.info( 'Concurrency set at %s', concurrency ); 273 | 274 | _index2.default.Promise.map( scripts, function( script ) { 275 | var num = place++; 276 | 277 | logger.verbose( 'Running script #%d, %s.', num, script ); 278 | 279 | var instance = manager.add( script, false ); 280 | 281 | instances.push( instance ); 282 | 283 | if( watch ) { 284 | instance.watch( true ); 285 | 286 | var onChange = function onChange() { 287 | logger.verbose( 'Re-running script #%d, %s', num, script ); 288 | run_script( instance, script, num ); 289 | }; 290 | 291 | if( typeof debounce === 'number' ) { 292 | onChange = _lodash2.default.debounce( onChange, debounce ); 293 | } 294 | 295 | instance.on( 'change', onChange ); 296 | } 297 | 298 | return run_script( instance, script, num ); 299 | }, { 300 | concurrency: concurrency 301 | 302 | } ).catch( onError ).then( function() { 303 | logger.log( 'All scripts successfully executed in %s', diff_ms( script_start ) ); 304 | 305 | if( _commander2.default.close ) { 306 | process.exit( EXIT_SUCCESS ); 307 | } 308 | } ); 309 | })(); 310 | } else { 311 | _commander2.default.help(); 312 | } 313 | }; 314 | 315 | var _commander = require( 'commander' ); 316 | 317 | var _commander2 = _interopRequireDefault( _commander ); 318 | 319 | var _module = require( 'module' ); 320 | 321 | var _module2 = _interopRequireDefault( _module ); 322 | 323 | var _glob = require( 'glob' ); 324 | 325 | var _glob2 = _interopRequireDefault( _glob ); 326 | 327 | var _path = require( 'path' ); 328 | 329 | var _path2 = _interopRequireDefault( _path ); 330 | 331 | var _package = require( './../package.json' ); 332 | 333 | var _package2 = _interopRequireDefault( _package ); 334 | 335 | var _lodash = require( 'lodash' ); 336 | 337 | var _lodash2 = _interopRequireDefault( _lodash ); 338 | 339 | var _defaults = require( './../build/modern/defaults.js' ); 340 | 341 | var _cli = require( './tools/cli.js' ); 342 | 343 | var _index = require( './../build/modern/index.js' ); 344 | 345 | var _index2 = _interopRequireDefault( _index ); 346 | 347 | function _interopRequireDefault( obj ) { 348 | return obj && obj.__esModule ? obj : {default: obj}; 349 | } 350 | 351 | /** 352 | * Created by Aaron on 6/30/2015. 353 | */ 354 | 355 | var constants = process.binding( 'constants' ); 356 | 357 | function diff_ms( start ) { 358 | var _process$hrtime = process.hrtime( start ); 359 | 360 | var seconds = _process$hrtime[0]; 361 | var nanoseconds = _process$hrtime[1]; 362 | 363 | 364 | var milliseconds = seconds * 1e3 + nanoseconds * 1e-6; 365 | 366 | return milliseconds.toFixed( 3 ) + 'ms'; 367 | } 368 | 369 | var ms_pattern = /([0-9]+)([A-Z]+)?/i; 370 | 371 | function toMilliseconds( str ) { 372 | var match = str.match( ms_pattern ); 373 | 374 | if( match ) { 375 | var i = parseInt( match[1] ); 376 | 377 | if( !isNaN( i ) ) { 378 | var u = match[2]; 379 | 380 | if( typeof u === 'string' ) { 381 | u = u.toLowerCase(); 382 | } else if( u === void 0 ) { 383 | return i; 384 | } 385 | 386 | switch( u ) { 387 | case 'ms': 388 | return i; 389 | case 's': 390 | return i * 1000; 391 | case 'm': 392 | return i * 60 * 1000; 393 | case 'h': 394 | return i * 60 * 60 * 1000; 395 | case 'd': 396 | return i * 24 * 60 * 60 * 1000; 397 | case 'y': 398 | return i * 365 * 24 * 60 * 60 * 1000; 399 | default: 400 | return i; 401 | } 402 | } 403 | } 404 | 405 | return null; 406 | } 407 | 408 | function parseIntOrInfinity( value ) { 409 | if( value.toLowerCase() === 'infinity' ) { 410 | return Infinity; 411 | } else { 412 | return parseInt( value ); 413 | } 414 | } 415 | 416 | //Process status codes 417 | var EXIT_SUCCESS = 0, 418 | EXIT_FAILURE = 1; 419 | 420 | _commander2.default.version( _package2.default.version ).usage( '[options] files...' ) 421 | .option( '-d, --dir ', 'Directory to run Scriptor in' ) 422 | .option( '-c, --concurrency ', 'Limit asynchronous script concurrency to n (default: 10)' ) 423 | .option( '-q, --close', 'End the process when all scripts finish' ) 424 | .option( '-w, --watch', 'Watch scripts for changes and re-run them when changed' ) 425 | .option( '-p, --propagate', 'Propagate changes upwards when watching scripts' ) 426 | .option( '-l, --long_stack_traces', 'Display long stack trace for errors' ) 427 | .option( '-r, --repeat ', 'Run script n times (in parallel if possible)' ) 428 | .option( '-u, --unique', 'Only run unique scripts (will ignore duplicates in file arguments)' ).option( '--debounce ', 429 | 'Wait n milliseconds for debounce on file watching events (default: ' + _defaults.default_debounceMaxWait + 'ms)' ) 430 | .option( '--use_strict', 'Enforce strict mode' ) 431 | .option( '--max_listeners ', 'Set the maximum number of listeners on any particular script' ) 432 | .option( '-v, --verbose [n]', 'Print out extra status information (0 - normal, 1 - info, 2 - verbose)' ) 433 | .option( '--cork', 'Cork stdout before calling scripts' ).option( '-s, --silent', 'Do not echo anything' ) 434 | .option( '--no_signal', 'Do not intercept process signals' ).option( '--no_glob', 'Do not match glob patterns' ) 435 | .option( '--no_title', 'Do not set process title' ); 436 | -------------------------------------------------------------------------------- /bin/tools/cli.es6: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/5/2015. 3 | */ 4 | 5 | import assert from 'assert'; 6 | import {EventEmitter} from 'events'; 7 | import util from 'util'; 8 | 9 | function Enum( values ) { 10 | assert( typeof values === 'object' && !Array.isArray( values ), 'Enum values must be an object' ); 11 | 12 | let result = {}; 13 | 14 | for( let it in values ) { 15 | if( values.hasOwnProperty( it ) ) { 16 | let value = values[it]; 17 | 18 | result[it] = value; 19 | result[value] = it; 20 | } 21 | } 22 | 23 | return Object.freeze( result ); 24 | } 25 | 26 | export var LogLevel = Enum( { 27 | LOG_ERROR: -2, 28 | LOG_SILENT: -1, 29 | LOG_WARN: 0, 30 | LOG_NORMAL: 0, 31 | LOG_INFO: 1, 32 | LOG_VERBOSE: 2 33 | } ); 34 | 35 | function print_message( level, message ) { 36 | 37 | } 38 | 39 | export class Logger extends EventEmitter { 40 | _level = null; 41 | 42 | constructor( level = LogLevel.LOG_NORMAL ) { 43 | super(); 44 | 45 | this.level = level; 46 | } 47 | 48 | get level() { 49 | return this._level; 50 | } 51 | 52 | set level( value ) { 53 | value = Math.floor( value ); 54 | 55 | assert( !isNaN( value ), 'level must be a number' ); 56 | 57 | this._level = value; 58 | } 59 | 60 | error( format, ...args ) { 61 | this.do_log( LogLevel.LOG_ERROR, `ERROR: ${format}`, args ); 62 | } 63 | 64 | warn( format, ...args ) { 65 | this.do_log( LogLevel.LOG_WARN, `WARNING: ${format}`, args ); 66 | } 67 | 68 | log( format, ...args ) { 69 | this.do_log( LogLevel.LOG_NORMAL, `LOG: ${format}`, args ); 70 | } 71 | 72 | info( format, ...args ) { 73 | this.do_log( LogLevel.LOG_INFO, `INFO: ${format}`, args ); 74 | } 75 | 76 | verbose( format, ...args ) { 77 | this.do_log( LogLevel.LOG_VERBOSE, `VERBOSE: ${format}`, args ); 78 | } 79 | 80 | do_log( level, format, args ) { 81 | if( level <= this.level ) { 82 | var message = util.format( format, ...args ); 83 | 84 | if( level === LogLevel.LOG_ERROR ) { 85 | console.error( message ); 86 | 87 | } else if( level === LogLevel.LOG_WARN ) { 88 | console.warn( message ); 89 | 90 | } else { 91 | console.log( message ); 92 | } 93 | 94 | this.emit( LogLevel[level], message ); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /bin/tools/cli.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | 'use strict'; 26 | 27 | exports.__esModule = true; 28 | exports.Logger = exports.LogLevel = void 0; 29 | 30 | var _classCallCheck2 = require( 'babel-runtime/helpers/classCallCheck' ); 31 | 32 | var _classCallCheck3 = _interopRequireDefault( _classCallCheck2 ); 33 | 34 | var _createClass2 = require( 'babel-runtime/helpers/createClass' ); 35 | 36 | var _createClass3 = _interopRequireDefault( _createClass2 ); 37 | 38 | var _possibleConstructorReturn2 = require( 'babel-runtime/helpers/possibleConstructorReturn' ); 39 | 40 | var _possibleConstructorReturn3 = _interopRequireDefault( _possibleConstructorReturn2 ); 41 | 42 | var _inherits2 = require( 'babel-runtime/helpers/inherits' ); 43 | 44 | var _inherits3 = _interopRequireDefault( _inherits2 ); 45 | 46 | var _freeze = require( 'babel-runtime/core-js/object/freeze' ); 47 | 48 | var _freeze2 = _interopRequireDefault( _freeze ); 49 | 50 | var _typeof2 = require( 'babel-runtime/helpers/typeof' ); 51 | 52 | var _typeof3 = _interopRequireDefault( _typeof2 ); 53 | 54 | var _assert = require( 'assert' ); 55 | 56 | var _assert2 = _interopRequireDefault( _assert ); 57 | 58 | var _events = require( 'events' ); 59 | 60 | var _util = require( 'util' ); 61 | 62 | var _util2 = _interopRequireDefault( _util ); 63 | 64 | function _interopRequireDefault( obj ) { 65 | return obj && obj.__esModule ? obj : {default: obj}; 66 | } 67 | 68 | function Enum( values ) { 69 | (0, _assert2.default)( 70 | (typeof values === 'undefined' ? 'undefined' : (0, _typeof3.default)( values )) === 'object' && !Array.isArray( values ), 71 | 'Enum values must be an object' ); 72 | 73 | var result = {}; 74 | 75 | for( var it in values ) { 76 | if( values.hasOwnProperty( it ) ) { 77 | var value = values[it]; 78 | 79 | result[it] = value; 80 | result[value] = it; 81 | } 82 | } 83 | 84 | return (0, _freeze2.default)( result ); 85 | } 86 | /** 87 | * Created by Aaron on 7/5/2015. 88 | */ 89 | 90 | var LogLevel = exports.LogLevel = Enum( { 91 | LOG_ERROR: -2, 92 | LOG_SILENT: -1, 93 | LOG_WARN: 0, 94 | LOG_NORMAL: 0, 95 | LOG_INFO: 1, 96 | LOG_VERBOSE: 2 97 | } ); 98 | 99 | function print_message( level, message ) { 100 | } 101 | 102 | var Logger = exports.Logger = function( _EventEmitter ) { 103 | (0, _inherits3.default)( Logger, _EventEmitter ); 104 | 105 | function Logger() { 106 | var level = arguments.length <= 0 || arguments[0] === void 0 ? LogLevel.LOG_NORMAL : arguments[0]; 107 | (0, _classCallCheck3.default)( this, Logger ); 108 | 109 | var _this = (0, _possibleConstructorReturn3.default)( this, _EventEmitter.call( this ) ); 110 | 111 | _this._level = null; 112 | 113 | 114 | _this.level = level; 115 | return _this; 116 | } 117 | 118 | Logger.prototype.error = function error( format ) { 119 | for( var _len = arguments.length, args = Array( _len > 1 ? _len - 1 : 0 ), _key = 1; _key < _len; _key++ ) { 120 | args[_key - 1] = arguments[_key]; 121 | } 122 | 123 | this.do_log( LogLevel.LOG_ERROR, 'ERROR: ' + format, args ); 124 | }; 125 | 126 | Logger.prototype.warn = function warn( format ) { 127 | for( var _len2 = arguments.length, args = Array( _len2 > 1 ? _len2 - 1 : 0 ), _key2 = 1; _key2 < _len2; _key2++ ) { 128 | args[_key2 - 1] = arguments[_key2]; 129 | } 130 | 131 | this.do_log( LogLevel.LOG_WARN, 'WARNING: ' + format, args ); 132 | }; 133 | 134 | Logger.prototype.log = function log( format ) { 135 | for( var _len3 = arguments.length, args = Array( _len3 > 1 ? _len3 - 1 : 0 ), _key3 = 1; _key3 < _len3; _key3++ ) { 136 | args[_key3 - 1] = arguments[_key3]; 137 | } 138 | 139 | this.do_log( LogLevel.LOG_NORMAL, 'LOG: ' + format, args ); 140 | }; 141 | 142 | Logger.prototype.info = function info( format ) { 143 | for( var _len4 = arguments.length, args = Array( _len4 > 1 ? _len4 - 1 : 0 ), _key4 = 1; _key4 < _len4; _key4++ ) { 144 | args[_key4 - 1] = arguments[_key4]; 145 | } 146 | 147 | this.do_log( LogLevel.LOG_INFO, 'INFO: ' + format, args ); 148 | }; 149 | 150 | Logger.prototype.verbose = function verbose( format ) { 151 | for( var _len5 = arguments.length, args = Array( _len5 > 1 ? _len5 - 1 : 0 ), _key5 = 1; _key5 < _len5; _key5++ ) { 152 | args[_key5 - 1] = arguments[_key5]; 153 | } 154 | 155 | this.do_log( LogLevel.LOG_VERBOSE, 'VERBOSE: ' + format, args ); 156 | }; 157 | 158 | Logger.prototype.do_log = function do_log( level, format, args ) { 159 | if( level <= this.level ) { 160 | var message = _util2.default.format.apply( _util2.default, [format].concat( args ) ); 161 | 162 | if( level === LogLevel.LOG_ERROR ) { 163 | console.error( message ); 164 | } else if( level === LogLevel.LOG_WARN ) { 165 | console.warn( message ); 166 | } else { 167 | console.log( message ); 168 | } 169 | 170 | this.emit( LogLevel[level], message ); 171 | } 172 | }; 173 | 174 | (0, _createClass3.default)( Logger, [{ 175 | key: 'level', 176 | get: function get() { 177 | return this._level; 178 | }, 179 | set: function set( value ) { 180 | value = Math.floor( value ); 181 | 182 | (0, _assert2.default)( !isNaN( value ), 'level must be a number' ); 183 | 184 | this._level = value; 185 | } 186 | }] ); 187 | return Logger; 188 | }( _events.EventEmitter ); 189 | -------------------------------------------------------------------------------- /build/defaults.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | 'use strict'; 26 | 27 | exports.__esModule = true; 28 | /** 29 | * Created by Aaron on 7/4/2015. 30 | */ 31 | 32 | // This is the default amount of time any file watchers should debounce events for 33 | var default_max_debounceMaxWait = exports.default_max_debounceMaxWait = 50; 34 | 35 | //This chunk of code is prepended to scripts before they are compiled so the define function can be made available to it 36 | var AMD_Header = exports.AMD_Header = 37 | "if(typeof define !== 'function' && typeof module.define === 'function') {var define = module.define;}"; 38 | 39 | //These are the default dependencies that all AMD scripts should have. They are appended to any other given dependencies 40 | var default_dependencies = exports.default_dependencies = ['require', 'exports', 'module', 'imports']; 41 | -------------------------------------------------------------------------------- /build/error.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | 'use strict'; 26 | 27 | exports.__esModule = true; 28 | exports.normalizeError = normalizeError; 29 | /** 30 | * Created by Aaron on 7/4/2015. 31 | */ 32 | 33 | /* 34 | * This turns generic errors into something like would be produced by require.js and almond.js 35 | * 36 | * Probably not fully necessary, but eh. 37 | * */ 38 | function normalizeError( id, type ) { 39 | var err = arguments.length <= 2 || arguments[2] === void 0 ? {} : arguments[2]; 40 | 41 | if( Array.isArray( err.requireModules ) && !Array.isArray( id ) && err.requireModules.indexOf( id ) === -1 ) { 42 | err.requireModules.push( id ); 43 | } else { 44 | err.requireModules = Array.isArray( id ) ? id : [id]; 45 | } 46 | 47 | err.requireType = err.requireType || type; 48 | 49 | err.message = (err.message || '') + ' - ' + id; 50 | 51 | return err; 52 | } 53 | -------------------------------------------------------------------------------- /build/extensions.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | "use strict"; 26 | 27 | exports.__esModule = true; 28 | 29 | var _bluebird = require( "bluebird" ); 30 | 31 | var _bluebird2 = _interopRequireDefault( _bluebird ); 32 | 33 | var _fs = require( "fs" ); 34 | 35 | var _utils = require( "./utils.js" ); 36 | 37 | function _interopRequireDefault( obj ) { 38 | return obj && obj.__esModule ? obj : {default: obj}; 39 | } 40 | 41 | var readFileAsync = _bluebird2.default.promisify( _fs.readFile ); 42 | 43 | /* 44 | * These are basically asynchronous versions of the default .js and .json extension handlers found in node/lib/module.js, 45 | * but the .js one also goes ahead and inject the AMD header described in ./defaults.js 46 | * */ 47 | 48 | /** 49 | * Created by Aaron on 7/5/2015. 50 | */ 51 | 52 | exports.default = { 53 | '.js': function js( module, filename ) { 54 | return readFileAsync( filename ).then( _utils.injectAMDAndStripBOM ).then( function( src ) { 55 | module._compile( src.toString( 'utf-8' ), filename ); 56 | 57 | return src; 58 | } ); 59 | }, 60 | '.json': function json( module, filename ) { 61 | return readFileAsync( filename ).then( _utils.stripBOM ).then( function( src ) { 62 | try { 63 | module.exports = JSON.parse( src.toString( 'utf-8' ) ); 64 | } catch( err ) { 65 | err.message = filename + ': ' + err.message; 66 | throw err; 67 | } 68 | 69 | return src; 70 | } ); 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | "use strict"; 26 | 27 | exports.__esModule = true; 28 | 29 | var _bluebird = require( "bluebird" ); 30 | 31 | var _bluebird2 = _interopRequireDefault( _bluebird ); 32 | 33 | var _script = require( "./script.js" ); 34 | 35 | var _script2 = _interopRequireDefault( _script ); 36 | 37 | var _source_script = require( "./source_script.js" ); 38 | 39 | var _source_script2 = _interopRequireDefault( _source_script ); 40 | 41 | var _text_script = require( "./text_script.js" ); 42 | 43 | var _text_script2 = _interopRequireDefault( _text_script ); 44 | 45 | var _manager = require( "./manager.js" ); 46 | 47 | var _manager2 = _interopRequireDefault( _manager ); 48 | 49 | var _utils = require( "./utils.js" ); 50 | 51 | var utils = _interopRequireWildcard( _utils ); 52 | 53 | function _interopRequireWildcard( obj ) { 54 | if( obj && obj.__esModule ) { 55 | return obj; 56 | } else { 57 | var newObj = {}; 58 | if( obj != null ) { 59 | for( var key in obj ) { 60 | if( Object.prototype.hasOwnProperty.call( obj, key ) ) { 61 | newObj[key] = obj[key]; 62 | } 63 | } 64 | } 65 | newObj.default = obj; 66 | return newObj; 67 | } 68 | } 69 | 70 | function _interopRequireDefault( obj ) { 71 | return obj && obj.__esModule ? obj : {default: obj}; 72 | } 73 | 74 | /** 75 | * Created by Aaron on 7/5/2015. 76 | */ 77 | 78 | /* 79 | * NOTE: This file uses ES7 export extensions 80 | * */ 81 | 82 | var Scriptor = { 83 | Promise: _bluebird2.default, 84 | Script: _script2.default, 85 | SourceScript: _source_script2.default, 86 | TextScript: _text_script2.default, 87 | Manager: _manager2.default, 88 | load: _script.load, 89 | compile: _source_script.compile, 90 | utils: utils 91 | }; 92 | 93 | //Provide a circular reference to Scriptor from Script 94 | _script2.default.Scriptor = Scriptor; 95 | 96 | exports.default = Scriptor; 97 | -------------------------------------------------------------------------------- /build/manager.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | "use strict"; 26 | 27 | exports.__esModule = true; 28 | 29 | var _map = require( "babel-runtime/core-js/map" ); 30 | 31 | var _map2 = _interopRequireDefault( _map ); 32 | 33 | var _classCallCheck2 = require( "babel-runtime/helpers/classCallCheck" ); 34 | 35 | var _classCallCheck3 = _interopRequireDefault( _classCallCheck2 ); 36 | 37 | var _createClass2 = require( "babel-runtime/helpers/createClass" ); 38 | 39 | var _createClass3 = _interopRequireDefault( _createClass2 ); 40 | 41 | var _possibleConstructorReturn2 = require( "babel-runtime/helpers/possibleConstructorReturn" ); 42 | 43 | var _possibleConstructorReturn3 = _interopRequireDefault( _possibleConstructorReturn2 ); 44 | 45 | var _inherits2 = require( "babel-runtime/helpers/inherits" ); 46 | 47 | var _inherits3 = _interopRequireDefault( _inherits2 ); 48 | 49 | var _module = require( "module" ); 50 | 51 | var _module2 = _interopRequireDefault( _module ); 52 | 53 | var _assert = require( "assert" ); 54 | 55 | var _assert2 = _interopRequireDefault( _assert ); 56 | 57 | var _path = require( "path" ); 58 | 59 | var _utils = require( "./utils.js" ); 60 | 61 | var _script = require( "./script.js" ); 62 | 63 | var _script2 = _interopRequireDefault( _script ); 64 | 65 | function _interopRequireDefault( obj ) { 66 | return obj && obj.__esModule ? obj : {default: obj}; 67 | } 68 | 69 | /* 70 | * This is a modification of Script which allows it to be spawned and managed by a Manager instance. 71 | * 72 | * It provides extra handling for including other scripts, which are also loaded into the owning manager. 73 | * */ 74 | 75 | var ManagedScript = function( _Script ) { 76 | (0, _inherits3.default)( ManagedScript, _Script ); 77 | 78 | function ManagedScript( manager, filename, parent ) { 79 | (0, _classCallCheck3.default)( this, ManagedScript ); 80 | 81 | var _this = (0, _possibleConstructorReturn3.default)( this, _Script.call( this, filename, parent ) ); 82 | 83 | _this._manager = null; 84 | 85 | 86 | _this._manager = manager; 87 | 88 | //When a script is renamed, it should be reassigned in the manager 89 | //Otherwise, when it's accessed at the new location, the manager just creates a new script 90 | _this.on( 'rename', function( event, oldname, newname ) { 91 | var scripts = _this._manager.scripts; 92 | 93 | 94 | scripts.set( newname, scripts.get( oldname ) ); 95 | scripts.delete( oldname ); 96 | } ); 97 | return _this; 98 | } 99 | 100 | ManagedScript.prototype.include = function include( filename ) { 101 | var _this2 = this; 102 | 103 | var load = arguments.length <= 1 || arguments[1] === void 0 ? false : arguments[1]; 104 | 105 | //make sure filename can be relative to the current script 106 | var real_filename = (0, _path.resolve)( this.baseUrl, filename ); 107 | 108 | //Since add doesn't do anything to already existing scripts, but does return a script, 109 | //it can take care of the lookup or adding at the same time. Two birds with one lookup. 110 | var script = this._manager.add( real_filename ); 111 | 112 | //Since include can be used independently of reference, make sure it's loaded before returning 113 | //Otherwise, the returned script is in an incomplete state 114 | if( load && !script.loaded ) { 115 | script.reload(); 116 | } 117 | 118 | this.propagateFrom( script, 'change', function() { 119 | _this2.unload(); 120 | _this2.emit( 'change', _this2.filename ); 121 | } ); 122 | 123 | script.propagateEvents( this.isPropagatingEvents() ); 124 | 125 | return script; 126 | }; 127 | 128 | ManagedScript.prototype.close = function close( permanent ) { 129 | if( permanent ) { 130 | this._manager.scripts.delete( this.filename ); 131 | 132 | delete this['_manager']; 133 | } 134 | 135 | return _Script.prototype.close.call( this, permanent ); 136 | }; 137 | 138 | (0, _createClass3.default)( ManagedScript, [{ 139 | key: "manager", 140 | get: function get() { 141 | return this._manager; 142 | } 143 | }] ); 144 | return ManagedScript; 145 | }( _script2.default ); 146 | 147 | /* 148 | * This Manager class really just takes care of a Map instance of ManagedScripts and allows configuring them all at once 149 | * and automatically. 150 | * */ 151 | 152 | /** 153 | * Created by Aaron on 7/5/2015. 154 | */ 155 | 156 | var Manager = function() { 157 | function Manager( grandParent ) { 158 | (0, _classCallCheck3.default)( this, Manager ); 159 | this._debounceMaxWait = null; 160 | this._maxListeners = null; 161 | this._config = null; 162 | this._cwd = process.cwd(); 163 | this._scripts = new _map2.default(); 164 | this._parent = null; 165 | this._propagateEvents = false; 166 | this._unloadOnRename = null; 167 | 168 | this._parent = new _module2.default( 'ScriptManager', grandParent ); 169 | } 170 | 171 | Manager.prototype.cwd = function cwd() { 172 | return this._cwd; 173 | }; 174 | 175 | Manager.prototype.chdir = function chdir( value ) { 176 | this._cwd = (0, _path.resolve)( this.cwd(), value ); 177 | 178 | return this._cwd; 179 | }; 180 | 181 | Manager.prototype.setMaxListeners = function setMaxListeners( value ) { 182 | if( value !== null && value !== void 0 ) { 183 | 184 | value = Math.floor( value ); 185 | 186 | (0, _assert2.default)( !isNaN( value ), 'setMaxListeners must be passed a number' ); 187 | 188 | this._maxListeners = value; 189 | } else { 190 | this._maxListeners = null; 191 | } 192 | }; 193 | 194 | Manager.prototype.getMaxListeners = function getMaxListeners() { 195 | return this._maxListeners; 196 | }; 197 | 198 | Manager.prototype.config = function config( _config ) { 199 | var _this3 = this; 200 | 201 | this._config = (0, _utils.normalizeConfig)( _config ); 202 | 203 | this._scripts.forEach( function( script ) { 204 | script.config( _this3._config, true ); 205 | script.unload(); 206 | } ); 207 | }; 208 | 209 | Manager.prototype.propagateEvents = function propagateEvents() { 210 | var enable = arguments.length <= 0 || arguments[0] === void 0 ? true : arguments[0]; 211 | 212 | var wasPropagating = this._propagateEvents; 213 | 214 | this._propagateEvents = enable; 215 | 216 | if( wasPropagating && !enable ) { 217 | //immediately disable propagation by pretending it's already been propagated 218 | this._scripts.forEach( function( script ) { 219 | script.propagateEvents( false ); 220 | } ); 221 | } else if( !wasPropagating && enable ) { 222 | this._scripts.forEach( function( script ) { 223 | script.propagateEvents( true ); 224 | } ); 225 | } 226 | }; 227 | 228 | Manager.prototype._modifyScript = function _modifyScript( script ) { 229 | if( script !== void 0 ) { 230 | if( this._propagateEvents ) { 231 | script.propagateEvents(); 232 | } 233 | 234 | if( this.debounceMaxWait !== null && this.debounceMaxWait !== void 0 ) { 235 | script.debounceMaxWait = this.debounceMaxWait; 236 | } 237 | 238 | if( this._maxListeners !== null && this._maxListeners !== void 0 ) { 239 | script.setMaxListeners( this._maxListeners ); 240 | } 241 | 242 | if( this._config !== null && this._config !== void 0 ) { 243 | script.config( this._config, true ); 244 | } 245 | 246 | if( this._unloadOnRename !== null && this._unloadOnRename !== void 0 ) { 247 | script.unloadOnRename = this._unloadOnRename; 248 | } 249 | } 250 | 251 | return script; 252 | }; 253 | 254 | /* 255 | * this and Script.watch are basically no-ops if nothing is to be added or it's already being watched 256 | * but this functions as a way to add and/or get a script in one fell swoop. 257 | * Since evaluation of a script is lazy, watch is defaulted to true, since there is almost no performance hit 258 | * from watching a file. 259 | * */ 260 | 261 | 262 | Manager.prototype.add = function add( filename ) { 263 | var watch = arguments.length <= 1 || arguments[1] === void 0 ? true : arguments[1]; 264 | 265 | filename = (0, _path.resolve)( this.cwd(), filename ); 266 | 267 | var script = this._scripts.get( filename ); 268 | 269 | if( script === void 0 ) { 270 | script = new ManagedScript( this, null, this._parent ); 271 | 272 | script.load( filename, watch ); 273 | 274 | this._scripts.set( filename, script ); 275 | } 276 | 277 | this._modifyScript( script ); 278 | 279 | //Even if the script is added, this allows it to be watched, though not unwatched. 280 | //Unwatching still has to be done manually 281 | if( watch ) { 282 | script.watch(); 283 | } 284 | 285 | return script; 286 | }; 287 | 288 | /* 289 | * Removes a script from the manager. But closing it permenantly is optional, 290 | * as it may sometimes make sense to move it out of a manager and use it independently. 291 | * However, that is quite rare so close defaults to true 292 | * */ 293 | 294 | 295 | Manager.prototype.remove = function remove( filename ) { 296 | var close = arguments.length <= 1 || arguments[1] === void 0 ? true : arguments[1]; 297 | 298 | filename = (0, _path.resolve)( this.cwd(), filename ); 299 | 300 | var script = this._scripts.get( filename ); 301 | 302 | if( script !== void 0 ) { 303 | if( close ) { 304 | script.close(); 305 | } 306 | 307 | return this._scripts.delete( filename ); 308 | } 309 | 310 | return false; 311 | }; 312 | 313 | Manager.prototype.call = function call( filename ) { 314 | for( var _len = arguments.length, args = Array( _len > 1 ? _len - 1 : 0 ), _key = 1; _key < _len; _key++ ) { 315 | args[_key - 1] = arguments[_key]; 316 | } 317 | 318 | return this.apply( filename, args ); 319 | }; 320 | 321 | Manager.prototype.apply = function apply( filename, args ) { 322 | (0, _assert2.default)( Array.isArray( args ) ); 323 | 324 | var script = this.add( filename, false ); 325 | 326 | try { 327 | return script.apply( args ); 328 | } catch( err ) { 329 | this.remove( filename, true ); 330 | 331 | throw err; 332 | } 333 | }; 334 | 335 | Manager.prototype.get = function get( filename ) { 336 | filename = (0, _path.resolve)( this.cwd(), filename ); 337 | 338 | return this._modifyScript( this._scripts.get( filename ) ); 339 | }; 340 | 341 | //Make closing optional for the same reason as .remove 342 | 343 | 344 | Manager.prototype.clear = function clear() { 345 | var close = arguments.length <= 0 || arguments[0] === void 0 ? true : arguments[0]; 346 | 347 | if( close ) { 348 | this._scripts.forEach( function( script ) { 349 | script.close(); 350 | } ); 351 | } 352 | 353 | this._scripts.clear(); 354 | }; 355 | 356 | (0, _createClass3.default)( Manager, [{ 357 | key: "parent", 358 | get: function get() { 359 | return this._parent; 360 | } 361 | }, { 362 | key: "scripts", 363 | get: function get() { 364 | return this._scripts; 365 | } 366 | }, { 367 | key: "debounceMaxWait", 368 | get: function get() { 369 | return this._debounceMaxWait; 370 | }, 371 | set: function set( time ) { 372 | if( time !== null && time !== void 0 ) { 373 | time = Math.floor( time ); 374 | 375 | (0, _assert2.default)( !isNaN( time ), 'debounceMaxWait must be set to a number' ); 376 | 377 | this._debounceMaxWait = time; 378 | } else { 379 | this._debounceMaxWait = null; 380 | } 381 | } 382 | }, { 383 | key: "unloadOnRename", 384 | set: function set( value ) { 385 | this._unloadOnRename = !!value; 386 | }, 387 | get: function get() { 388 | return this._unloadOnRename; 389 | } 390 | }] ); 391 | return Manager; 392 | }(); 393 | 394 | exports.default = Manager; 395 | -------------------------------------------------------------------------------- /build/source_script.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | "use strict"; 26 | 27 | exports.__esModule = true; 28 | 29 | var _classCallCheck2 = require( "babel-runtime/helpers/classCallCheck" ); 30 | 31 | var _classCallCheck3 = _interopRequireDefault( _classCallCheck2 ); 32 | 33 | var _createClass2 = require( "babel-runtime/helpers/createClass" ); 34 | 35 | var _createClass3 = _interopRequireDefault( _createClass2 ); 36 | 37 | var _possibleConstructorReturn2 = require( "babel-runtime/helpers/possibleConstructorReturn" ); 38 | 39 | var _possibleConstructorReturn3 = _interopRequireDefault( _possibleConstructorReturn2 ); 40 | 41 | var _inherits2 = require( "babel-runtime/helpers/inherits" ); 42 | 43 | var _inherits3 = _interopRequireDefault( _inherits2 ); 44 | 45 | exports.compile = compile; 46 | 47 | var _bluebird = require( "bluebird" ); 48 | 49 | var _bluebird2 = _interopRequireDefault( _bluebird ); 50 | 51 | var _path = require( "path" ); 52 | 53 | var _utils = require( "./utils.js" ); 54 | 55 | var _script = require( "./script.js" ); 56 | 57 | var _script2 = _interopRequireDefault( _script ); 58 | 59 | function _interopRequireDefault( obj ) { 60 | return obj && obj.__esModule ? obj : {default: obj}; 61 | } 62 | 63 | /* 64 | * The SourceScript variation is a Script that allows loading from in-memory strings. These are always assumed to be normal JavaScript. 65 | * */ 66 | 67 | /** 68 | * Created by Aaron on 7/5/2015. 69 | */ 70 | 71 | function compile( src ) { 72 | var watch = arguments.length <= 1 || arguments[1] === void 0 ? true : arguments[1]; 73 | var parent = arguments.length <= 2 || arguments[2] === void 0 ? null : arguments[2]; 74 | 75 | var script = new SourceScript( src, parent ); 76 | 77 | if( watch ) { 78 | script.watch(); 79 | } 80 | 81 | return script; 82 | } 83 | 84 | var SourceScript = function( _Script ) { 85 | (0, _inherits3.default)( SourceScript, _Script ); 86 | 87 | function SourceScript( src ) { 88 | var parent = arguments.length <= 1 || arguments[1] === void 0 ? module : arguments[1]; 89 | (0, _classCallCheck3.default)( this, SourceScript ); 90 | 91 | var _this = (0, _possibleConstructorReturn3.default)( this, _Script.call( this, null, parent ) ); 92 | 93 | _this._onChange = null; 94 | 95 | 96 | if( src !== void 0 && src !== null ) { 97 | _this.load( src ); 98 | } 99 | return _this; 100 | } 101 | 102 | SourceScript.prototype._do_load = function _do_load() { 103 | var _this2 = this; 104 | 105 | if( !this.loading || this._loadingText && !this.textMode ) { 106 | this.unload(); 107 | 108 | if( !this.textMode ) { 109 | this._do_setup(); 110 | 111 | this._loading = true; 112 | this._loadingText = false; 113 | 114 | if( this._willWatch ) { 115 | try { 116 | this._do_watch( this._watchPersistent ); 117 | } catch( err ) { 118 | this._loading = false; 119 | 120 | this.emit( 'loading_error', err ); 121 | } 122 | } 123 | 124 | this.source( 'utf-8' ).then( function( src ) { 125 | _this2._script._compile( src, _this2.filename ); 126 | 127 | _this2._script.loaded = true; 128 | 129 | _this2._loading = false; 130 | 131 | _this2.emit( 'loaded', _this2._script.exports ); 132 | }, function( err ) { 133 | _this2._loading = false; 134 | 135 | _this2.emit( 'loading_error', err ); 136 | } ); 137 | } else { 138 | this._loading = true; 139 | this._loadingText = true; 140 | 141 | if( this._willWatch ) { 142 | try { 143 | this._do_watch( this._watchPersistent ); 144 | } catch( err ) { 145 | this._loading = false; 146 | this._loadingText = false; 147 | 148 | this.emit( 'loading_src_error', err ); 149 | } 150 | } 151 | 152 | this.source( 'utf-8' ).then( function( src ) { 153 | _this2._script.loaded = true; 154 | 155 | _this2._loading = false; 156 | _this2._loadingText = false; 157 | 158 | _this2.emit( 'loaded', _this2.loaded ); 159 | }, function( err ) { 160 | _this2._loading = false; 161 | _this2._loadingText = false; 162 | 163 | _this2.emit( 'loading_error', err ); 164 | } ); 165 | } 166 | } 167 | }; 168 | 169 | SourceScript.prototype._normalizeSource = function _normalizeSource( src ) { 170 | assert( typeof src === 'string' || Buffer.isBuffer( src ), 'Factory source must return string or Buffer as value' ); 171 | 172 | src = (0, _utils.stripBOM)( src ); 173 | 174 | if( !this.textMode && _script2.default.extensions_enabled ) { 175 | src = (0, _utils.injectAMD)( src ); 176 | } 177 | 178 | if( Buffer.isBuffer( src ) && typeof encoding === 'string' ) { 179 | src = src.toString( encoding ); 180 | } 181 | 182 | return src; 183 | }; 184 | 185 | SourceScript.prototype.source = function source( encoding ) { 186 | var _this3 = this; 187 | 188 | if( typeof this._source === 'function' ) { 189 | return (0, _utils.tryPromise)( this._source() ).then( function( src ) { 190 | return _this3._normalizeSource( src ); 191 | } ); 192 | } else { 193 | try { 194 | var src = this._normalizeSource( this._source ); 195 | 196 | return _bluebird2.default.resolve( src ); 197 | } catch( err ) { 198 | return _bluebird2.default.reject( err ); 199 | } 200 | } 201 | }; 202 | 203 | SourceScript.prototype.load = function load( src ) { 204 | var watch = arguments.length <= 1 || arguments[1] === void 0 ? true : arguments[1]; 205 | 206 | assert( typeof src === 'string' || Buffer.isBuffer( src ) || typeof src === 'function', 207 | 'Source must be a string, Buffer or factory function' ); 208 | 209 | this.close( false ); 210 | 211 | this._source = typeof src === 'string' ? new Buffer( src ) : src; 212 | 213 | if( watch ) { 214 | this.watch(); 215 | } 216 | 217 | this.emit( 'change', 'change', this.filename ); 218 | 219 | return this; 220 | }; 221 | 222 | (0, _createClass3.default)( SourceScript, [{ 223 | key: "filename", 224 | get: function get() { 225 | return this._script.filename; 226 | }, 227 | set: function set( value ) { 228 | this._script.filename = value; 229 | } 230 | }, { 231 | key: "baseUrl", 232 | get: function get() { 233 | return (0, _path.dirname)( this.filename ); 234 | }, 235 | set: function set( value ) { 236 | value = (0, _path.dirname)( value ); 237 | 238 | this.filename = value + (0, _path.basename)( this.filename ); 239 | } 240 | }, { 241 | key: "watched", 242 | get: function get() { 243 | return typeof this._onChange === 'function'; 244 | } 245 | }] ); 246 | return SourceScript; 247 | }( _script2.default ); 248 | 249 | exports.default = SourceScript; 250 | -------------------------------------------------------------------------------- /build/text_script.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | "use strict"; 26 | 27 | exports.__esModule = true; 28 | 29 | var _classCallCheck2 = require( "babel-runtime/helpers/classCallCheck" ); 30 | 31 | var _classCallCheck3 = _interopRequireDefault( _classCallCheck2 ); 32 | 33 | var _createClass2 = require( "babel-runtime/helpers/createClass" ); 34 | 35 | var _createClass3 = _interopRequireDefault( _createClass2 ); 36 | 37 | var _possibleConstructorReturn2 = require( "babel-runtime/helpers/possibleConstructorReturn" ); 38 | 39 | var _possibleConstructorReturn3 = _interopRequireDefault( _possibleConstructorReturn2 ); 40 | 41 | var _inherits2 = require( "babel-runtime/helpers/inherits" ); 42 | 43 | var _inherits3 = _interopRequireDefault( _inherits2 ); 44 | 45 | var _script = require( "./script.js" ); 46 | 47 | var _script2 = _interopRequireDefault( _script ); 48 | 49 | function _interopRequireDefault( obj ) { 50 | return obj && obj.__esModule ? obj : {default: obj}; 51 | } 52 | 53 | /* 54 | * This is just a variation on the normal script that forces it to always think it's in text mode 55 | * */ 56 | 57 | var TextScript = function( _Script ) { 58 | (0, _inherits3.default)( TextScript, _Script ); 59 | 60 | function TextScript() { 61 | (0, _classCallCheck3.default)( this, TextScript ); 62 | return (0, _possibleConstructorReturn3.default)( this, _Script.apply( this, arguments ) ); 63 | } 64 | 65 | (0, _createClass3.default)( TextScript, [{ 66 | key: "textMode", 67 | get: function get() { 68 | return true; 69 | } 70 | }] ); 71 | return TextScript; 72 | }( _script2.default ); 73 | /** 74 | * Created by Aaron on 7/7/2015. 75 | */ 76 | 77 | exports.default = TextScript; 78 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | /**** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Aaron Trent 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | ****/ 25 | "use strict"; 26 | 27 | exports.__esModule = true; 28 | exports.isAbsolutePath = void 0; 29 | exports.isAbsoluteOrRelative = isAbsoluteOrRelative; 30 | exports.bind = bind; 31 | exports.stripBOM = stripBOM; 32 | exports.injectAMD = injectAMD; 33 | exports.injectAMDAndStripBOM = injectAMDAndStripBOM; 34 | exports.parseDefine = parseDefine; 35 | exports.isThenable = isThenable; 36 | exports.tryPromise = tryPromise; 37 | exports.tryReject = tryReject; 38 | exports.isGenerator = isGenerator; 39 | exports.isGeneratorFunction = isGeneratorFunction; 40 | exports.parseDeps = parseDeps; 41 | exports.normalizeConfig = normalizeConfig; 42 | 43 | var _bluebird = require( "bluebird" ); 44 | 45 | var _bluebird2 = _interopRequireDefault( _bluebird ); 46 | 47 | var _lodash = require( "lodash" ); 48 | 49 | var _ = _interopRequireWildcard( _lodash ); 50 | 51 | var _path = require( "path" ); 52 | 53 | var _defaults = require( "./defaults.js" ); 54 | 55 | function _interopRequireWildcard( obj ) { 56 | if( obj && obj.__esModule ) { 57 | return obj; 58 | } else { 59 | var newObj = {}; 60 | if( obj != null ) { 61 | for( var key in obj ) { 62 | if( Object.prototype.hasOwnProperty.call( obj, key ) ) { 63 | newObj[key] = obj[key]; 64 | } 65 | } 66 | } 67 | newObj.default = obj; 68 | return newObj; 69 | } 70 | } 71 | 72 | function _interopRequireDefault( obj ) { 73 | return obj && obj.__esModule ? obj : {default: obj}; 74 | } 75 | 76 | /** 77 | * Created by Aaron on 7/4/2015. 78 | */ 79 | 80 | var AMD_Header_Buffer = new Buffer( _defaults.AMD_Header ); 81 | 82 | var isAbsolutePath = exports.isAbsolutePath = 83 | typeof _path.isAbsolute === 'function' ? _path.isAbsolute : function isAbsolutePath( filepath ) { 84 | return normalize( filepath ) === (0, _path.resolve)( filepath ); 85 | }; 86 | 87 | function isAbsoluteOrRelative( filepath ) { 88 | return filepath.charAt( 0 ) === '.' || isAbsolutePath( filepath ); 89 | } 90 | 91 | function bind( func ) { 92 | for( var _len = arguments.length, args = Array( _len > 1 ? _len - 1 : 0 ), _key = 1; _key < _len; _key++ ) { 93 | args[_key - 1] = arguments[_key]; 94 | } 95 | 96 | var bound = func.bind.apply( func, args ); 97 | 98 | //This assumes sub-functions can handle their own scopes 99 | //or are closures that take that into account 100 | for( var it in func ) { 101 | if( func.hasOwnProperty( it ) ) { 102 | bound[it] = func[it]; 103 | } 104 | } 105 | 106 | return bound; 107 | } 108 | 109 | /* 110 | * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) 111 | * because the buffer-to-string conversion in `fs.readFileSync()` 112 | * translates it to FEFF, the UTF-16 BOM. 113 | * */ 114 | function stripBOM( content ) { 115 | if( Buffer.isBuffer( content ) && content.length >= 2 && content[0] === 0xFE && content[1] === 0xFF ) { 116 | content = content.slice( 2 ); 117 | } else if( typeof content === 'string' && content.charCodeAt( 0 ) === 0xFEFF ) { 118 | content = content.slice( 1 ); 119 | } 120 | 121 | return content; 122 | } 123 | 124 | function injectAMD( content ) { 125 | if( Buffer.isBuffer( content ) ) { 126 | return Buffer.concat( [AMD_Header_Buffer, content] ); 127 | } else if( typeof content === 'string' ) { 128 | return _defaults.AMD_Header + content; 129 | } else { 130 | return content; 131 | } 132 | } 133 | 134 | function injectAMDAndStripBOM( content ) { 135 | return injectAMD( stripBOM( content ) ); 136 | } 137 | 138 | function parseDefine( id, deps, factory ) { 139 | //This argument parsing code is taken from amdefine 140 | if( Array.isArray( id ) ) { 141 | factory = deps; 142 | deps = id; 143 | id = void 0; 144 | } else if( typeof id !== 'string' ) { 145 | factory = id; 146 | id = deps = void 0; 147 | } 148 | 149 | if( deps !== void 0 && !Array.isArray( deps ) ) { 150 | factory = deps; 151 | deps = void 0; 152 | } 153 | 154 | if( deps === void 0 ) { 155 | deps = _defaults.default_dependencies; 156 | } else { 157 | deps = deps.concat( _defaults.default_dependencies ); 158 | } 159 | 160 | return [id, deps, factory]; 161 | } 162 | 163 | /* 164 | * Simple test to see if an object is a Promise instance 165 | * */ 166 | function isThenable( obj ) { 167 | return obj !== void 0 && obj !== null && (obj instanceof _bluebird2.default || typeof obj.then === 'function'); 168 | } 169 | 170 | /* 171 | * "coerces" a value into a Promise. 172 | * */ 173 | function tryPromise( value ) { 174 | if( isThenable( value ) ) { 175 | return value; 176 | } else { 177 | return _bluebird2.default.resolve( value ); 178 | } 179 | } 180 | 181 | /* 182 | * Executes a function synchronously and returns a rejected Promise if it throws 183 | * */ 184 | function tryReject( func, context ) { 185 | try { 186 | for( var _len2 = arguments.length, args = Array( _len2 > 2 ? _len2 - 2 : 0 ), _key2 = 2; _key2 < _len2; _key2++ ) { 187 | args[_key2 - 2] = arguments[_key2]; 188 | } 189 | 190 | return tryPromise( func.apply( context, args ) ); 191 | } catch( err ) { 192 | return _bluebird2.default.reject( err ); 193 | } 194 | } 195 | 196 | /* 197 | * The next few do exactly what they say on the tin. 198 | * */ 199 | 200 | function isGenerator( obj ) { 201 | return 'function' === typeof obj.next && 'function' === typeof obj.throw; 202 | } 203 | 204 | function isGeneratorFunction( obj ) { 205 | if( !obj.constructor ) { 206 | return false; 207 | } else if( 'GeneratorFunction' === obj.constructor.name || 'GeneratorFunction' === obj.constructor.displayName ) { 208 | return true; 209 | } else { 210 | return isGenerator( obj.constructor.prototype ); 211 | } 212 | } 213 | 214 | function isNull( value ) { 215 | return value === null || value === void 0; 216 | } 217 | 218 | function toPosix( filepath ) { 219 | return filepath.replace( '\\', '/' ); 220 | } 221 | 222 | function parseDeps( deps, paths ) { 223 | if( _.isObject( deps ) && !Array.isArray( deps ) ) { 224 | return _.map( deps, function( v, k ) { 225 | 226 | //If deps have a specified path, use that instead, but only if it hasn't already been defined 227 | if( !paths[k] ) { 228 | paths[k] = v; 229 | } 230 | 231 | return k; 232 | } ); 233 | } else if( !Array.isArray( deps ) ) { 234 | return [/*No valid dependencies*/]; 235 | } else { 236 | return deps; 237 | } 238 | } 239 | 240 | /* 241 | * This is pretty complicated. 242 | * 243 | * TODO: Explain this. 244 | * */ 245 | function normalizeConfig( config ) { 246 | var isObject = _.isObject( config ) && !Array.isArray( config ); 247 | 248 | var defaultConfig = { 249 | baseUrl: '.' + _path.posix.sep, //String 250 | paths: {}, //Object 251 | deps: [], //Array 252 | shim: {} //Object 253 | }; 254 | 255 | if( isObject ) { 256 | config = _.defaults( config, defaultConfig ); 257 | } else { 258 | return defaultConfig; 259 | } 260 | 261 | //Normalize baseUrl 262 | if( typeof config.baseUrl === 'string' ) { 263 | config.baseUrl = _path.posix.normalize( toPosix( config.baseUrl ) ); 264 | } else { 265 | config.baseUrl = defaultConfig.baseUrl; 266 | } 267 | 268 | //Make sure paths is an object 269 | if( !_.isObject( config.paths ) || Array.isArray( config.paths ) ) { 270 | config.paths = defaultConfig.paths; 271 | } 272 | 273 | //Make sure shim is an object 274 | if( !_.isObject( config.shim ) || Array.isArray( config.shim ) ) { 275 | config.shim = defaultConfig.shim; 276 | } 277 | 278 | //Normalize deps 279 | config.deps = parseDeps( config.deps, config.paths ); 280 | 281 | //Normalize shims 282 | config.shim = _.chain( config.shim ).mapValues( function( shim ) { 283 | if( Array.isArray( shim ) ) { 284 | return { 285 | deps: parseDeps( shim, config.paths ) 286 | }; 287 | } else if( _.isObject( shim ) && typeof shim.exports === 'string' ) { 288 | return { 289 | deps: parseDeps( shim.deps, config.paths ), 290 | exports: shim.exports 291 | }; 292 | } 293 | } ).omit( isNull ).value(); 294 | 295 | //Normalize paths 296 | config.paths = _.chain( config.paths ).mapValues( function( p ) { 297 | if( typeof p === 'string' ) { 298 | return toPosix( p ); 299 | } 300 | } ).omit( isNull ).value(); 301 | 302 | return config; 303 | } 304 | -------------------------------------------------------------------------------- /docs/amd.md: -------------------------------------------------------------------------------- 1 | Scriptor Asynchronous Module Definitions 2 | ======================================== 3 | 4 | One of the original motivations for Scriptor 2.x was to create a script environment where the code is completely compatible with requirejs and AMD, so as to use some of the same exact code between servers and clients. 5 | 6 | To do this, Scriptor provides a rudimentary but complete AMD implementation built right into every Script. 7 | 8 | As show in the [API Documentation](https://github.com/novacrazy/scriptor/blob/master/docs/api.md), Script instances have a [`define`](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#define-idefinefunction) and [`require`](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#require-irequirefunction) functions that can be used to define modules and require dependencies. They work together as well to evaluate defined modules and resolve module dependencies, too. 9 | 10 | Additionally, `define` is injected into the global `module` variable. However, that isn't exactly useful by itself, so by using custom extension handlers Scriptor can move that value into the 'global' namespace for each module. It does this by injecting this bit of code into the beginning of each script: 11 | ```javascript 12 | if(typeof define !== 'function' && typeof module.define === 'function') { 13 | var define = module.define; 14 | } 15 | ``` 16 | 17 | Which is similar to amdefine's header, but uses `module.define` instead. 18 | 19 | This allows scripts to use AMD style declarations natively, like so: 20 | ```javascript 21 | define(['fs'], function(fs, require) { 22 | var file = fs.readFileSync('something.txt', 'utf-8'); 23 | 24 | require('assert', function(assert) { 25 | assert(true); 26 | }); 27 | }); 28 | ``` 29 | 30 | Notice how the `require` passed into the factory function calls a callback instead of just returning the value. That's because (at least if the async build is being used), there is a distinct possibility that requiring a module could be asynchronous, and that callback should be executed whenever it's finally loaded. 31 | 32 | Additionally, for the async build of scriptor, `require` returns a Promise in addition to the callback interface. This is useful for coroutines, which is discussed further down. 33 | 34 | It should be noted that Scriptor's `require` is ONLY the one passed into the factory function, and NOT the global `require`. The global `require` is still Node's standard CommonJS require and is still synchronous. It's recommended to avoid external requires and just use the AMD form for dependencies. 35 | 36 | Although requirejs doesn't support it, Scriptor's async build supports coroutines almost everywhere. For example: 37 | ```javascript 38 | define(['promisify!fs'], function*(fs, require) { 39 | var file = yield fs.readFileAsync('something.txt', 'utf-8'); 40 | var assert = yield require('assert'); 41 | 42 | assert(true); 43 | }); 44 | ``` 45 | 46 | Using coroutines makes asynchronous code look much better while retaining much of the same performance as normal asynchronous code. 47 | 48 | Internally, Scriptor uses Bluebird coroutines for asynchronous coroutine handling, so all the things that can be yielded for that can also be yielded in Scriptor factories, transform functions and even `main` functions returned from the factory. It's all automatic. 49 | 50 | Scriptor's AMD implementation supports named submodules withing modules, any amount of dependencies, integration with Scriptor and Scriptor Managers, synchronous and asynchronous dependency resolution and plugins, and really anything requirejs does. However, if there is a feature missing or that you'd like to request, please post an issue about it and it will be reviewed and added post-haste. 51 | 52 | Speaking of plugins, Scriptor comes with a few built-in plugins. For the synchronous and asynchronous builds, there is the 'include!' plugin, which uses the [`.include`](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#includefilename--string-load--boolean---script) function to include Script instances directly. 53 | 54 | For just the asynchronous build, there is also the 'promisify!' plugin, which duplicates the dependency object and passes it through [bluebird's promisifyAll or promisify functions](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisification) 55 | 56 | -------------------------------------------------------------------------------- /docs/cli.md: -------------------------------------------------------------------------------- 1 | Scriptor Command Line Interface Documentation 2 | ============================================= 3 | 4 | Scriptor comes with a nifty little command line tool for executing simple scripts. 5 | 6 | Here is the help text for the binary: 7 | ``` 8 | $scriptor -h 9 | 10 | Usage: scriptor [options] files... 11 | 12 | Options: 13 | 14 | -h, --help output usage information 15 | -V, --version output the version number 16 | -d, --dir Directory to run Scriptor in 17 | -a, --async Run scripts asynchronously 18 | -c, --concurrency Limit script concurrency to n when executed asynchronously (default: max_recursion + 1) 19 | -q, --close End the process when all scripts finish 20 | -w, --watch Watch scripts for changes and re-run them when changed 21 | -p, --propagate Propagate changes upwards when watching scripts 22 | -l, --long_stack_traces Display long stack trace for asynchronous errors 23 | -r, --repeat Run script n times (in parallel if async) 24 | -u, --unique Only run unique scripts (will ignore duplicates in file arguments) 25 | --debounce Wait n milliseconds for debounce on file watching events (default: 50ms) 26 | --use_strict Enforce strict mode 27 | --max_listeners Set the maximum number of listeners on any particular script 28 | --max_recursion Set the maximum recursion depth of scripts (default: 9) 29 | -v, --verbose [n] Print out extra status information (0 - normal, 1 - info, 2 - verbose) 30 | --cork Cork stdout before calling scripts 31 | -s, --silent Do not echo anything 32 | --no_ext Disable use of custom extensions with AMD injection 33 | --no_signal Do not intercept process signals 34 | --no_glob Do not match glob patterns 35 | --no_title Do not set process title 36 | ``` 37 | 38 | ##Internals 39 | 40 | Internally, when valid scripts or globs are passed to the Scriptor CLI, it will create a Manager instance for either the synchronous or asynchronous builds, and run the scripts through those. 41 | 42 | Using [`Manager.chdir()`](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#chdirdir--string---string), it sets the current working directory for each script as set by the `--dir ` option. 43 | 44 | ##Concurrency and Recursion 45 | 46 | It is important to note that `--max_recursion` and `--concurrency` can conflict with each other occasionally. 47 | 48 | For example, passing the arguments `scriptor -alpw --max_recursion 4 --concurrency Infinity --repeat 10 index.js` will fail. It tries to repeat execution of `index.js` 10 times, running them all at the same time. Since they are the same script instance internally, it thinks that because the recursion counter has incremented the number of times it has executed, that it has overflown the maximum recursion limit, throwing an error. 49 | 50 | Although that's kind of a rare situation, since `--repeat` probably won't be used much, it's still good to keep in mind. Additionally, co-recursive scripts may overflow the maximum recursion limit if run concurrently. 51 | 52 | ##Logging 53 | 54 | Scriptor rolls its own tiny logger that supports various levels of verbosity. 55 | 56 | The supported levels are: 57 | 58 | | Level | Meaning | 59 | |:---------:|:------------------| 60 | | -2 | Errors only | 61 | | -1 | Silenced | 62 | | 0 | Normal/Warnings | 63 | | 1 | Extra information | 64 | | 2 | Verbose | 65 | -------------------------------------------------------------------------------- /docs/examples/example 1/app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | //NOTE: Coroutines only work for the asynchronous build 3 | define( ['koa'], function*( koa, require ) { 4 | //require returns a promise, which can be yielded to the coroutine 5 | var router = yield require( 'koa-router' ); 6 | 7 | return function() { 8 | var app = koa(); 9 | 10 | //Configure other parts of the app 11 | 12 | app.use( router( app ) ); 13 | return app; 14 | }; 15 | } ); 16 | -------------------------------------------------------------------------------- /docs/examples/example 1/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | var Scriptor = require( 'scriptor/async' ); 3 | Scriptor.enableCustomExtensions(); //Enable asynchronous file loading and AMD injection 4 | 5 | var main = new Scriptor.Script( 'app.js' ); 6 | 7 | main.call().then( function( app ) { 8 | app.listen( 8080 ); 9 | } ); 10 | -------------------------------------------------------------------------------- /docs/examples/example 2/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | var assert = require( 'assert' ); 3 | var Scriptor = require( 'scriptor/sync' ); 4 | 5 | var main = new Scriptor.Script( 'test.js' ); 6 | assert.strictEqual( main.exports(), 42 ); 7 | -------------------------------------------------------------------------------- /docs/examples/example 2/test.js: -------------------------------------------------------------------------------- 1 | module.exports = 42; 2 | -------------------------------------------------------------------------------- /docs/examples/example 3/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | var Scriptor = require( 'scriptor' ); 3 | Scriptor.installCustomExtensions(); 4 | 5 | var script = new Scriptor.Script( 'something.js' ); 6 | 7 | script.imports = { 8 | message: 'Hello, World!' 9 | }; 10 | 11 | script.call(); 12 | -------------------------------------------------------------------------------- /docs/examples/example 3/something.js: -------------------------------------------------------------------------------- 1 | //something.js 2 | define( ['imports'], function( imports ) { 3 | console.log( imports.message ); //Hello, World! 4 | } ); 5 | -------------------------------------------------------------------------------- /docs/examples/example 4/index.js: -------------------------------------------------------------------------------- 1 | var Scriptor = require( 'scriptor/sync' ); 2 | var Reference = Scriptor.Reference; 3 | 4 | var ref = Reference.resolve( 32 ); 5 | 6 | ref = ref.transform( function( left ) { 7 | return left.value() + 10; 8 | } ); 9 | 10 | console.log( ref.value() ); //42 11 | -------------------------------------------------------------------------------- /docs/examples/example 5/index.js: -------------------------------------------------------------------------------- 1 | var Scriptor = require( 'scriptor/async' ); 2 | var Reference = Scriptor.Reference; 3 | 4 | var ref = Reference.resolve( 'World!' ); 5 | 6 | ref = ref.transform( function*( left ) { 7 | return 'Hello, ' + (yield left.value()); 8 | } ); 9 | 10 | ref.value().then( console.log ); //Hello, World! 11 | -------------------------------------------------------------------------------- /docs/examples/example 6/index.js: -------------------------------------------------------------------------------- 1 | var Scriptor = require( 'scriptor/async' ); 2 | var Reference = Scriptor.Reference; 3 | 4 | var a = Reference.resolve( 'Hello, ' ); 5 | var b = Reference.resolve( 'World!' ); 6 | 7 | var ref = a.join( b, function*( left, right ) { 8 | return (yield left.value()) + (yield right.value()); 9 | } ); 10 | 11 | ref.value().then( console.log ); //Hello, World! 12 | -------------------------------------------------------------------------------- /docs/examples/example 7/index.js: -------------------------------------------------------------------------------- 1 | var Scriptor = require( 'scriptor/sync' ); 2 | var Reference = Scriptor.Reference; 3 | 4 | var refs = []; 5 | 6 | for( var i = 0; i < 10; i++ ) { 7 | refs.push( Reference.resolve( i ) ); 8 | } 9 | 10 | var root = Reference.join_all( refs, function( left, right ) { 11 | return left.value() + ', ' + right.value(); 12 | } ); 13 | 14 | console.log( root.value() ); //0, 1, 2, 3, 4, 5, 6, 7, 8, 9 15 | -------------------------------------------------------------------------------- /docs/examples/example 8/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | var Scriptor = require( 'scriptor/sync' ); 3 | 4 | var manager = new Scriptor.Manager( module ); 5 | 6 | var res = manager.call( 'something.js' ); 7 | 8 | console.log( res.value ); //100 9 | -------------------------------------------------------------------------------- /docs/examples/example 8/something.js: -------------------------------------------------------------------------------- 1 | //somescript.js 2 | module.define( { 3 | value: 100 4 | } ); 5 | -------------------------------------------------------------------------------- /docs/examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "examples", 3 | "version": "1.0.0", 4 | "description": "Example files for Scriptor", 5 | "main": "null", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Aaron Trent ", 10 | "license": "MIT", 11 | "private": true, 12 | "dependencies": { 13 | "koa": "^0.18.1", 14 | "koa-router": "^4.2.0", 15 | "scriptor": "^2.0.0-beta.25" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/internal_api.md: -------------------------------------------------------------------------------- 1 | Scriptor Internals 2 | ================== 3 | 4 | Internally, Scriptor is not as simple as the standard API would have one believe. To simplify things, Scriptor takes advantage of TypeScript's inheritance to simplify tasks and modularity. 5 | 6 | ##Table of contents 7 | - [Script inheritance](#script-inheritance) 8 | - [EventPropagator](#eventpropagator) extends EventEmitter 9 | - [ScriptBase](#scriptbase) extends [EventPropagator](#eventpropagator) 10 | - [AMDScript](#amdscript) extends [ScriptBase](#scriptbase) 11 | - [Script](#script) extends [AMDScript](#amdscript) 12 | - [ScriptAdapter](#scriptadapter) extends [Script](#script) 13 | 14 | - [`Common` module](#common-module) 15 | - [`isAbsolutePath(filepath : string)`](#isabsolutepathfilepath--string---boolean) 16 | - [`isAbsoluteOrRelative(filepath : string)`](#isabsoluteorrelativefilepath--string---boolean) 17 | - [`bind(func : Function, ...args : any[])`](#bindfunc--function-args--any---function) 18 | - [`parseDefine(id : any, deps? : any, factory? : any)`](#parsedefineid--any-deps--any-factory--any---id-deps-factory) 19 | - [`normalizeError(id : any, type : string, err : any)`](#normalizeerrorid--any-type--string-err--any---error) 20 | - [`removeFromParent(script : IModule)`](#removefromparentscript--imodule) 21 | - [`stripBOM(content : string)`](#stripbomcontent--string---string) 22 | - [`injectAMD(content : string)`](#injectamdcontent--string---string) 23 | - [`injectAMDAndStripBOM(content : string)`](#injectamdandstripbomcontent--string---string) 24 | - [`shallowCloneObject(obj : any)`](#shallowcloneobjectobj--any---object) 25 | - [`AMD_Header`](#amd_header---string) 26 | - [`default_max_recursion`](#default_max_recursion---number) 27 | - [`default_dependencies`](#default_dependencies---string) 28 | 29 | ##Script Inheritance 30 | 31 | So to separate out concerns for script components, Scriptor uses multiple layers of inheritance for implementing some things. I, the author, know that isn't necessarily a good pattern to go by, especially in statically typed and compiled languages like C++, but it works just fine for JavaScript/TypeScript where there is no runtime cost for doing so. 32 | 33 | #####`EventPropagator` 34 | 35 | Located in `base.js`, this is a simple class that takes care of event propagation. 36 | 37 | #####`ScriptBase` 38 | 39 | This is the base class for actual scripts. It takes care of the internal `IModule` instance and handles basic functionality like filenames, parent and children management, various flags and also a few helper functions for execution protection, including recursion protection. 40 | 41 | ----- 42 | 43 | #####`AMDScript` 44 | 45 | This is the class that handles the `define` and `require` function primarily. It contains various values and functions to facilitate those. 46 | 47 | ----- 48 | 49 | #####`Script` 50 | 51 | Actual implementation of file loading, compilation, evaluation and watching. Most of it. 52 | 53 | See [`Scriptor.Script`](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#script) for full documentation of everything in this class. 54 | 55 | ----- 56 | 57 | #####`ScriptAdapter` 58 | 59 | The ScriptAdapter class is a tiny extension of the normal Script that is used with Managers. Managers never actually use or return normal Scripts, they use ScriptAdapters, since the ScriptAdapter is extended to handle `include` and other managed-script specific stuff. 60 | 61 | ----- 62 | 63 | ##`Common` module 64 | 65 | To reduce duplicate code, a lot of simple functions that work in both the synchronous and asynchronous builds are placed in the `Common` module. 66 | 67 | This `Common` module is exported to Scriptor as `Scriptor.common`, note the capitalization difference. 68 | 69 | ----- 70 | 71 | #####`isAbsolutePath(filepath : string)` -> `boolean` 72 | 73 | Returns true if the path given is absolute. 74 | 75 | In Node 0.11 and above, this uses `path.isAbsolute`, but for older versions it uses `path.resolve(filepath) === path.normalize(filepath)`, which is a cheap hack that works most of the time. 76 | 77 | ----- 78 | 79 | #####`isAbsoluteOrRelative(filepath : string)` -> `boolean` 80 | 81 | Just checks if the filepath is relative or absolute 82 | 83 | ```javascript 84 | function isAbsoluteOrRelative( filepath : string ) : boolean { 85 | return filepath.charAt( 0 ) === '.' || isAbsolutePath( filepath ); 86 | } 87 | ``` 88 | 89 | ----- 90 | 91 | #####`bind(func : Function, ...args : any[])` -> `Function` 92 | 93 | Binds a function to the arguments provided, including `this` as the first argument provided. 94 | 95 | It then copies references to any attached properties to the newly bound function. 96 | 97 | This is how `define` and `require` can have additional functions attached to them. 98 | 99 | ----- 100 | 101 | #####`parseDefine(id : any, deps? : any, factory? : any)` -> `[id, deps, factory]` 102 | 103 | Parses the raw `define` arguments, which are sort of variadic and out of order, and returns a tuple with the parsed arguments in them, or null for those that don't exist. 104 | 105 | For example `define('myModule', {})` will result in the parsed arguments `['myModule', null, {}]` 106 | 107 | This code is mostly taken from [amdefine](https://github.com/jrburke/amdefine/blob/ebcc612e924ab08e3e993238cbb42251b17d27a2/amdefine.js#L248) 108 | 109 | ----- 110 | 111 | #####`normalizeError(id : any, type : string, err : any)` -> `Error` 112 | 113 | Takes an error object and normalizes it according to how requirejs does it, with the `requireModules` and `requireType` values added to it. 114 | 115 | This is used with `require` for error normalization. 116 | 117 | ----- 118 | 119 | #####`removeFromParent(script : IModule)` 120 | 121 | Takes a script and searches its parent for itself, then removes itself from the parent. 122 | 123 | Used in [`Script.close(true)`](https://github.com/novacrazy/scriptor/blob/master/docs/api.md#closepermanent--boolean) 124 | 125 | ----- 126 | 127 | #####`stripBOM(content : string)` -> `string` 128 | 129 | If the string starts with `0xFEFF`, a Byte Order Marker, that character is removed from the string. 130 | 131 | ----- 132 | 133 | #####`injectAMD(content : string)` -> `string` 134 | 135 | Prepends [`AMD_Header`](#amd_header---string) to `content` and returns it. 136 | 137 | ----- 138 | 139 | #####`injectAMDAndStripBOM(content : string)` -> `string` 140 | 141 | [Removes BOM](#stripbomcontent--string---string) and [Injects AMD Header](#injectamdcontent--string---string) in one function. 142 | 143 | ----- 144 | 145 | #####`shallowCloneObject(obj : any)` -> `Object` 146 | 147 | Shallow clones an object. Used in the `promisify!` plugin to prevent polluting global state. 148 | 149 | ----- 150 | 151 | #####`AMD_Header` -> `string` 152 | 153 | ```javascript 154 | if(typeof define !== 'function' && typeof module.define === 'function') { 155 | var define = module.define; 156 | } 157 | ``` 158 | 159 | ----- 160 | 161 | #####`default_max_recursion` -> `number` 162 | 163 | Defaults to 9 164 | 165 | ----- 166 | 167 | #####`default_dependencies` -> `string[]` 168 | 169 | Defaults to `['require', 'exports', 'module', 'imports']` 170 | -------------------------------------------------------------------------------- /docs/recipes/async-plugin/README.md: -------------------------------------------------------------------------------- 1 | Scriptor AMD Plugins 2 | ==================== 3 | 4 | Scriptor strives to have a complete AMD implementation, and part of that is plugins. 5 | 6 | For the complete plugin API, see [The RequireJS docs page on the matter](http://requirejs.org/docs/plugins.html) 7 | 8 | It should be noted that Scriptor only supports a subset of the full plugin API, as it does not need to execute any of the functions for optimization purposes. 9 | 10 | Scriptor will only acknowledge the following plugin format: 11 | ```typescript 12 | interface IOnLoadFunction { 13 | (module : any); 14 | onError(err : Error); 15 | fromText(source : string|Reference); 16 | } 17 | 18 | interface IAMDPlugin { 19 | load( name : string, require : IRequireFunction, onLoad : IOnLoadFunction, config : any ); 20 | normalize( name : string, normalize : Function ) : string; 21 | } 22 | ``` 23 | 24 | `write`, `writeFile`, `pluginBuilder`, and `onLayerEnd` are not supported and will be ignored. 25 | -------------------------------------------------------------------------------- /docs/recipes/async-plugin/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | define( ['./local-text!lorum-ipsum.txt'], function( lorumIpsum ) { 6 | console.log( lorumIpsum ); 7 | } ); 8 | -------------------------------------------------------------------------------- /docs/recipes/async-plugin/local-text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | define( ['promisify!fs'], function( fs ) { 6 | return { 7 | load: function( name, require, onLoad, config ) { 8 | fs.readFileAsync( name, 'utf-8' ).then( onLoad ); 9 | } 10 | }; 11 | } ); 12 | -------------------------------------------------------------------------------- /docs/recipes/async-plugin/lorum-ipsum.txt: -------------------------------------------------------------------------------- 1 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 2 | -------------------------------------------------------------------------------- /docs/recipes/custom extension handlers/README.md: -------------------------------------------------------------------------------- 1 | Custom Extension Handlers 2 | ========================= 3 | 4 | ##Topics 5 | * [What is an extension handler?](#what-is-an-extension-handler) 6 | * [So what makes Scriptor's custom extensions special?](#so-what-makes-scriptors-custom-extensions-special) 7 | * [Adding new custom extension handlers](#adding-new-custom-extension-handlers) 8 | 9 | 10 | One of the unique features about Scriptor is the ability to load and compile files using its own custom extension handlers. This allows it to load files asynchronously and inject code into them to add additional features. However, not many probably know exactly how Node.js and io.js load and compile files to begin with, so custom extensions can seem quite foreign, but they really aren't that bad. 11 | 12 | ###What is an extension handler? 13 | 14 | Node.js can load multiple types of files, like `.js`, `.json` and `.node` binary extensions. It does this through specific functions for each file extension. Like `index.js` will be handled by the `.js` extension handler found in `require.extensions['.js']`, which is quite simple. Here is is below: 15 | 16 | ```javascript 17 | // Native extension for .js 18 | Module._extensions['.js'] = function(module, filename) { 19 | var content = fs.readFileSync(filename, 'utf8'); 20 | module._compile(stripBOM(content), filename); 21 | }; 22 | ``` 23 | 24 | All it does is read the file in, strip the BOM characters off and uses `module._compile` to evaluate the script. 25 | 26 | Internally, `module._compile` uses the `vm` module to execute the new code in an isolated context within a function wrapper. That's where `exports` and `require` come from, among other things. But the thing to take away is that if you pass valid source code to `module._compile`, it will compile and run it. 27 | 28 | ###So what makes Scriptor's custom extensions special? 29 | 30 | Well, Scriptor prefers to 're-invent the wheel' so that it can both load files asynchronously and inject a bit of code that places `module.define` into the global namespace of that script, making AMD modules much easier to use. 31 | 32 | However, a drawback of this is that in order to have Scriptor handle other file extensions, they must be installed manually. 33 | 34 | So first, let's look at the basic synchronous module loading for Scriptor: 35 | ```javascript 36 | Scriptor.extensions['.js'] = function(module, filename) { 37 | var content = fs.readFileSync( filename, 'utf8' ); 38 | module._compile( Common.injectAMD( Common.stripBOM( content ) ), filename ); 39 | }; 40 | ``` 41 | 42 | Which is pretty much identical to the built-in handler for Node.js, but it also injects that AMD code mentioned previously. 43 | 44 | But the real usefullness shows in the asynchronous build, as shown below: 45 | ```javascript 46 | //Promisify using bluebird 47 | var readFile = Promise.promisify(fs.readFile); 48 | Scriptor.extensions['.js'] = function(module, filename) { 49 | return readFile( filename, 50 | 'utf-8' ).then( Common.stripBOM ).then( Common.injectAMD ).then( function(content) { 51 | module._compile( content, filename ); 52 | } ); 53 | }; 54 | 55 | Scriptor.extensions['.json'] = function(module, filename) { 56 | return readFile( filename, 'utf-8' ).then( Common.stripBOM ).then( function(content) { 57 | try { 58 | module.exports = JSON.parse( content ); 59 | } 60 | catch( err ) { 61 | err.message = filename + ': ' + err.message; 62 | throw err; 63 | } 64 | } ); 65 | }; 66 | ``` 67 | 68 | As shown, these extensions load the files asynchronously and returns a Promise for when they are done compiling. 69 | 70 | ###Adding new custom extension handlers 71 | 72 | Of course all this complexity would be useless if it wasn't possible to add your own handlers. 73 | 74 | To do so, you basically emulate the above behavior for either the synchronous or asynchronous build extensions. 75 | 76 | For example, if you wanted to add a handler to asynchronously load React's `.jsx` file extension, it would look something like this: 77 | ```javascript 78 | define( ['Scriptor', 'promisify!fs', 'react-tools'], function(Scriptor, fs, React) { 79 | Scriptor.extensions['.jsx'] = function(module, filename) { 80 | return fs.readFileAsync( filename, 'utf-8' ).then( function(content) { 81 | return Scriptor.common.injectAMDAndStripBOM( content ); 82 | 83 | } ).then( function(src) { 84 | return React.transform( src ); 85 | 86 | } ).then( function(src) { 87 | module._compile( src, filename ); 88 | } ); 89 | }; 90 | } ); 91 | ``` 92 | 93 | The dependency `Scriptor` is a special module in which Scriptor interprets should be itself, obviously. This also takes advantage of the internal api `Scriptor.Common`, which is described more in depth [HERE](https://github.com/novacrazy/scriptor/blob/master/docs/internal_api.md#common-module) 94 | -------------------------------------------------------------------------------- /docs/recipes/custom extension handlers/dot-require-async.js: -------------------------------------------------------------------------------- 1 | define( ['Scriptor', 'promisify!fs', 'dot'], function( Scriptor, fs, doT ) { 2 | Scriptor.extensions['.dot'] = function( module, filename ) { 3 | return fs.readFileAsync( filename, 'utf-8' ).then( function( content ) { 4 | if( content.charCodeAt( 0 ) === 0xFEFF ) { 5 | content = content.slice( 1 ); 6 | } 7 | 8 | doT.templateSettings.strip = false; 9 | 10 | try { 11 | module.exports = doT.template( content ); 12 | 13 | } catch( err ) { 14 | err.message = filename + ': ' + err.message; 15 | throw err; 16 | } 17 | } ); 18 | }; 19 | } ); 20 | -------------------------------------------------------------------------------- /docs/recipes/custom extension handlers/dot-require-sync.js: -------------------------------------------------------------------------------- 1 | define( ['Scriptor', 'fs', 'dot'], function( Scriptor, fs, doT ) { 2 | Scriptor.extensions['.dot'] = function( module, filename ) { 3 | var src = fs.readFileSync( filename, 'utf-8' ); 4 | src = Scriptor.common.stripBOM( src ); 5 | 6 | try { 7 | module.exports = doT.template( src ); 8 | 9 | } catch( err ) { 10 | err.message = filename + ': ' + err.message; 11 | throw err; 12 | } 13 | }; 14 | } ); 15 | -------------------------------------------------------------------------------- /docs/recipes/custom extension handlers/jsx-require-async.js: -------------------------------------------------------------------------------- 1 | define( ['Scriptor', 'promisify!fs', 'react-tools'], function( Scriptor, fs, React ) { 2 | Scriptor.extensions['.jsx'] = function( module, filename ) { 3 | return fs.readFileAsync( filename, 'utf-8' ).then( function( src ) { 4 | 5 | src = React.transform( Scriptor.common.stripBOM( src ) ); 6 | 7 | module._compile( Scriptor.common.injectAMD( src ), filename ); 8 | 9 | return src; 10 | } ); 11 | }; 12 | } ); 13 | -------------------------------------------------------------------------------- /docs/recipes/custom extension handlers/jsx-require-sync.js: -------------------------------------------------------------------------------- 1 | define( ['Scriptor', 'fs', 'react-tools'], function( Scriptor, fs, React ) { 2 | Scriptor.extensions['.jsx'] = function( module, filename ) { 3 | var src = fs.readFileSync( filename, 'utf-8' ); 4 | src = Scriptor.common.stripBOM( src ); 5 | src = React.transform( src ); 6 | module._compile( Scriptor.common.injectAMD( src ), filename ); 7 | return src; 8 | }; 9 | } ); 10 | -------------------------------------------------------------------------------- /docs/recipes/react-server/README.md: -------------------------------------------------------------------------------- 1 | Basic Server Using Scriptor 2 | =========================== 3 | 4 | The original motivation for Scriptor was to create a server that would automatically reload scripts when the files changed, so it would only make sense to include an example of that. 5 | 6 | To run it, simply download the directory via a git clone or whatever you prefer, then run `npm run setup`. To actually run the server, run `npm run server`. Please report any issues. 7 | 8 | The basic idea behind this is that a koa app controls the web part of it, and the react parts control the rendering of views. To do this, it uses a custom extension handler for the `.jsx` file extension, a factory module that keeps track of file changes while making sure there is always an up-to-date factory for the components, and a render module that takes a component factory and renders it to static markup, which is then passed back to koa for it to serve as the body of a reply. 9 | 10 | The factory module is probably the most interesting one, as it uses References and transform functions to lazily create factory functions out of raw components, but re-use old ones if the component files haven't changed. It's quite neat, if I do say so. 11 | 12 | ###Some things to note: 13 | * Views themselves export functions, while components export React components 14 | * This is so views have to be called, while components are simply used directly 15 | * I prefer doT templates for the higher HTML stuff because I can shove in other stuff that isn't really for React, like meta tags and IE-specific condition script inclusions and so forth. 16 | * Views and components should not use advanced Scriptor stuff like coroutines and built-in modules. They should generally be designed so that they can be wrapped up nicely with the RequireJS Optimizer and sent to the client to become live. 17 | * Using the references and includes in the factory and top stuff allows for Scriptor to reload those scripts when they change, effectively making it live-updating very easily. 18 | -------------------------------------------------------------------------------- /docs/recipes/react-server/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | //Define our module 6 | define( ['koa', 'koa-router', './lib/react-factory', './lib/react-render'], 7 | function*( koa, router, factory, render, require ) { 8 | 9 | //Add custom extension handlers for .jsx and .dot files 10 | yield require( ['./lib/jsx-require-async.js', './lib/dot-require-async.js'] ); 11 | 12 | //create a new koa app that will handle the web stuff 13 | var app = koa(); 14 | app.use( router( app ) ); 15 | 16 | //This is the outer HTML that wraps the react views 17 | //Notice we include it as a script, not as a module. 18 | //That allows it to be updated if modified, as Top.call will reload the script if needed 19 | var Top = yield require( 'include!./views/react-top.dot' ); 20 | 21 | //Redirect to index page 22 | app.get( '/', function*() { 23 | this.redirect( '/index.jsx' ); 24 | } ); 25 | 26 | //All .jsx files represent views, render them view rendering their factory functions then wrap it in the top HTML. 27 | app.get( /.*?\.jsx$/g, function*() { 28 | this.type = 'text/html'; 29 | //Call the Top script and pass in the correct values 30 | this.body = yield Top.call( { 31 | title: this.path, 32 | content: render( yield factory( '../views' + this.path ), this ) 33 | } ); 34 | } ); 35 | 36 | //Create HTTP server 37 | var server = app.listen( 8080 ); 38 | 39 | //Make sure the server closes upon reloading, otherwise we get an 'Error: port in use' error. 40 | module.on( 'unload', function() { 41 | server.close(); 42 | } ); 43 | } ); 44 | -------------------------------------------------------------------------------- /docs/recipes/react-server/lib/dot-require-async.js: -------------------------------------------------------------------------------- 1 | define( ['Scriptor', 'promisify!fs', 'dot'], function( Scriptor, fs, doT ) { 2 | Scriptor.extensions['.dot'] = function( module, filename ) { 3 | return fs.readFileAsync( filename, 'utf-8' ).then( function( content ) { 4 | if( content.charCodeAt( 0 ) === 0xFEFF ) { 5 | content = content.slice( 1 ); 6 | } 7 | 8 | doT.templateSettings.strip = false; 9 | 10 | try { 11 | module.exports = doT.template( content ); 12 | 13 | } catch( err ) { 14 | err.message = filename + ': ' + err.message; 15 | throw err; 16 | } 17 | } ); 18 | }; 19 | } ); 20 | -------------------------------------------------------------------------------- /docs/recipes/react-server/lib/jsx-require-async.js: -------------------------------------------------------------------------------- 1 | define( ['Scriptor', 'promisify!fs', 'react-tools'], function( Scriptor, fs, React ) { 2 | Scriptor.extensions['.jsx'] = function( module, filename ) { 3 | return fs.readFileAsync( filename, 'utf-8' ).then( function( src ) { 4 | 5 | src = React.transform( Scriptor.common.stripBOM( src ) ); 6 | 7 | module._compile( Scriptor.common.injectAMD( src ), filename ); 8 | 9 | return src; 10 | } ); 11 | }; 12 | } ); 13 | -------------------------------------------------------------------------------- /docs/recipes/react-server/lib/react-factory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | define( ['react'], function( React, require ) { 6 | //Create a cache map to be used in storing the references 7 | var cache = new Map(); 8 | 9 | //Make sure whenever the script reloads that the cache map is cleared 10 | module.on( 'unload', function() { 11 | cache.clear(); 12 | } ); 13 | 14 | //Scriptor supports coroutines as the 'main' function of a script, so this takes advantage of that 15 | return function*( view ) { 16 | var ref; 17 | 18 | //If the view reference already exists, just return it 19 | if( cache.has( view ) ) { 20 | ref = cache.get( view ); 21 | 22 | } else { 23 | //Otherwise, require and include the view as a script 24 | var script = yield require( 'include!' + view ); 25 | 26 | //Use the script reference function to do a transform that creates a React factory 27 | ref = script.reference().transform( function*( left ) { 28 | return React.createFactory( yield left.value() ); 29 | } ); 30 | 31 | cache.set( view, ref ); 32 | } 33 | 34 | //Return the value (or a Promise to the value in this case). 35 | return ref.value(); 36 | }; 37 | } ); 38 | -------------------------------------------------------------------------------- /docs/recipes/react-server/lib/react-render.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | define( ['react'], function( React ) { 6 | return function( Content, options ) { 7 | return React.renderToStaticMarkup( Content( options ) ); 8 | }; 9 | } ); 10 | -------------------------------------------------------------------------------- /docs/recipes/react-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scriptor-react-server-example", 3 | "version": "1.0.0", 4 | "description": "Basic Server Using Scriptor ===========================", 5 | "main": "app.js", 6 | "scripts": { 7 | "setup": "npm install && npm install -g scriptor@latest", 8 | "server": "scriptor -awvlp --use_strict --debounce 500ms app.js" 9 | }, 10 | "author": "Aaron Trent ", 11 | "license": "MIT", 12 | "dependencies": { 13 | "dot": "^1.0.3", 14 | "koa": "^0.18.1", 15 | "koa-router": "^4.2.0", 16 | "react": "^0.13.1", 17 | "react-tools": "^0.13.1" 18 | }, 19 | "engines": { 20 | "iojs": ">= 1.0.0", 21 | "node": ">= 0.12.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/recipes/react-server/views/components/hello-world.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | define( ['react'], function( React ) { 6 | return React.createClass( { 7 | render: function() { 8 | return ( 9 |

Hello, World!

10 | ); 11 | } 12 | } ); 13 | } ); 14 | -------------------------------------------------------------------------------- /docs/recipes/react-server/views/index.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | define( ['react', './components/hello-world.jsx'], function( React, Hello ) { 6 | 7 | var Index = React.createClass( { 8 | render: function() { 9 | return ( 10 | 11 | ); 12 | } 13 | } ); 14 | 15 | return function() { 16 | return Index; 17 | }; 18 | } ); 19 | -------------------------------------------------------------------------------- /docs/recipes/react-server/views/page.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 3/22/2015. 3 | */ 4 | 5 | define( ['react', './components/hello-world.jsx'], function( React, Hello ) { 6 | var Page = React.createClass( { 7 | render: function() { 8 | return ( 9 |
10 | 11 | 12 |
13 |
14 |

This is some example text.

15 |
16 |
17 | ); 18 | } 19 | } ); 20 | 21 | return function() { 22 | return Page; 23 | }; 24 | } ); 25 | -------------------------------------------------------------------------------- /docs/recipes/react-server/views/react-top.dot: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{=it.title}} 5 | 6 | 7 |
8 | {{=it.content}} 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by novacrazy on 6/17/14. 3 | */ 4 | 5 | module.exports = require( './build/index.js' ).default; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scriptor", 3 | "version": "4.0.0-alpha.2", 4 | "description": "Scriptor is the ultimate library for dynamically loading, reloading and running scripts without having to restart the process.", 5 | "main": "index.js", 6 | "keywords": [ 7 | "async", 8 | "amd", 9 | "script", 10 | "lazy", 11 | "evaluation", 12 | "module", 13 | "dynamic", 14 | "updatable", 15 | "config", 16 | "transform", 17 | "promises", 18 | "coroutine" 19 | ], 20 | "bin": { 21 | "scriptor": "bin/scriptor" 22 | }, 23 | "author": "Aaron Trent ", 24 | "license": "MIT", 25 | "repository": { 26 | "type": "git", 27 | "url": "https://github.com/novacrazy/scriptor" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/novacrazy/scriptor/issues" 31 | }, 32 | "devDependencies": { 33 | "babel": "^6.5.2", 34 | "babel-plugin-check-es2015-constants": "^6.8.0", 35 | "babel-plugin-transform-async-to-module-method": "^6.8.0", 36 | "babel-plugin-transform-class-constructor-call": "^6.8.0", 37 | "babel-plugin-transform-class-properties": "^6.10.2", 38 | "babel-plugin-transform-decorators": "^6.8.0", 39 | "babel-plugin-transform-do-expressions": "^6.8.0", 40 | "babel-plugin-transform-es2015-arrow-functions": "^6.8.0", 41 | "babel-plugin-transform-es2015-block-scoped-functions": "^6.8.0", 42 | "babel-plugin-transform-es2015-block-scoping": "^6.10.1", 43 | "babel-plugin-transform-es2015-classes": "^6.9.0", 44 | "babel-plugin-transform-es2015-computed-properties": "^6.8.0", 45 | "babel-plugin-transform-es2015-destructuring": "^6.9.0", 46 | "babel-plugin-transform-es2015-for-of": "^6.8.0", 47 | "babel-plugin-transform-es2015-function-name": "^6.9.0", 48 | "babel-plugin-transform-es2015-literals": "^6.8.0", 49 | "babel-plugin-transform-es2015-modules-commonjs": "^6.10.3", 50 | "babel-plugin-transform-es2015-object-super": "^6.8.0", 51 | "babel-plugin-transform-es2015-parameters": "^6.9.0", 52 | "babel-plugin-transform-es2015-shorthand-properties": "^6.8.0", 53 | "babel-plugin-transform-es2015-spread": "^6.8.0", 54 | "babel-plugin-transform-es2015-sticky-regex": "^6.8.0", 55 | "babel-plugin-transform-es2015-template-literals": "^6.8.0", 56 | "babel-plugin-transform-es2015-typeof-symbol": "^6.8.0", 57 | "babel-plugin-transform-es2015-unicode-regex": "^6.11.0", 58 | "babel-plugin-transform-es5-property-mutators": "^6.8.0", 59 | "babel-plugin-transform-exponentiation-operator": "^6.8.0", 60 | "babel-plugin-transform-export-extensions": "^6.8.0", 61 | "babel-plugin-transform-function-bind": "^6.8.0", 62 | "babel-plugin-transform-object-rest-spread": "^6.8.0", 63 | "babel-plugin-transform-regenerator": "^6.9.0", 64 | "babel-plugin-transform-runtime": "^6.9.0", 65 | "babel-plugin-transform-strict-mode": "^6.8.0", 66 | "babel-plugin-transform-undefined-to-void": "^6.8.0", 67 | "babel-preset-es2015": "^6.9.0", 68 | "babel-preset-stage-0": "^6.5.0", 69 | "babel-preset-stage-3": "^6.11.0", 70 | "grunt": "^1.0.1", 71 | "grunt-babel": "^6.0.0", 72 | "grunt-banner": "^0.6.0", 73 | "grunt-contrib-clean": "^1.0.0", 74 | "mocha": "^2.5.3", 75 | "touch": "^1.0.0", 76 | "treeify": "^1.0.1" 77 | }, 78 | "scripts": { 79 | "build": "grunt default", 80 | "test": "npm run build && mocha -b test/build" 81 | }, 82 | "dependencies": { 83 | "babel-runtime": "^6.9.2", 84 | "bluebird": "^3.4.1", 85 | "commander": "^2.9.0", 86 | "event-propagator": "^1.0.0", 87 | "glob": "^7.0.5", 88 | "lodash": "^4.13.1", 89 | "promisify-events": "^1.1.0" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/defaults.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/4/2015. 3 | */ 4 | 5 | // This is the default amount of time any file watchers should debounce events for 6 | export var default_max_debounceMaxWait = 50; 7 | 8 | //This chunk of code is prepended to scripts before they are compiled so the define function can be made available to it 9 | export var AMD_Header = "if(typeof define !== 'function' && typeof module.define === 'function') {var define = module.define;}"; 10 | 11 | //These are the default dependencies that all AMD scripts should have. They are appended to any other given dependencies 12 | export var default_dependencies = ['require', 'exports', 'module', 'imports']; 13 | -------------------------------------------------------------------------------- /src/error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/4/2015. 3 | */ 4 | 5 | /* 6 | * This turns generic errors into something like would be produced by require.js and almond.js 7 | * 8 | * Probably not fully necessary, but eh. 9 | * */ 10 | export function normalizeError( id, type, err = {} ) { 11 | if( Array.isArray( err.requireModules ) 12 | && !Array.isArray( id ) 13 | && err.requireModules.indexOf( id ) === -1 ) { 14 | err.requireModules.push( id ); 15 | 16 | } else { 17 | err.requireModules = Array.isArray( id ) ? id : [id]; 18 | } 19 | 20 | err.requireType = err.requireType || type; 21 | 22 | err.message = `${err.message || ''} - ${id}`; 23 | 24 | return err; 25 | } 26 | -------------------------------------------------------------------------------- /src/extensions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/5/2015. 3 | */ 4 | 5 | import Promise from "bluebird"; 6 | import {readFile} from "fs"; 7 | import {stripBOM, injectAMDAndStripBOM} from "./utils.js"; 8 | 9 | let readFileAsync = Promise.promisify( readFile ); 10 | 11 | /* 12 | * These are basically asynchronous versions of the default .js and .json extension handlers found in node/lib/module.js, 13 | * but the .js one also goes ahead and inject the AMD header described in ./defaults.js 14 | * */ 15 | 16 | export default { 17 | '.js': ( module, filename ) => { 18 | return readFileAsync( filename ).then( injectAMDAndStripBOM ).then( src => { 19 | module._compile( src.toString( 'utf-8' ), filename ); 20 | 21 | return src; 22 | } ) 23 | }, 24 | '.json': ( module, filename ) => { 25 | return readFileAsync( filename ).then( stripBOM ).then( src => { 26 | try { 27 | module.exports = JSON.parse( src.toString( 'utf-8' ) ); 28 | 29 | } catch( err ) { 30 | err.message = filename + ': ' + err.message; 31 | throw err; 32 | } 33 | 34 | return src; 35 | } ); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/5/2015. 3 | */ 4 | 5 | /* 6 | * NOTE: This file uses ES7 export extensions 7 | * */ 8 | 9 | import Promise from "bluebird"; 10 | import Script, {load} from "./script.js"; 11 | import SourceScript, {compile} from "./source_script.js"; 12 | import TextScript from "./text_script.js"; 13 | import Manager from "./manager.js"; 14 | import * as utils from "./utils.js"; 15 | 16 | const Scriptor = { 17 | Promise, 18 | Script, 19 | SourceScript, 20 | TextScript, 21 | Manager, 22 | load, 23 | compile, 24 | utils 25 | }; 26 | 27 | //Provide a circular reference to Scriptor from Script 28 | Script.Scriptor = Scriptor; 29 | 30 | export default Scriptor; 31 | -------------------------------------------------------------------------------- /src/manager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/5/2015. 3 | */ 4 | 5 | import Module from "module"; 6 | import assert from "assert"; 7 | import {resolve} from "path"; 8 | import {normalizeConfig} from "./utils.js"; 9 | import Script from "./script.js"; 10 | 11 | /* 12 | * This is a modification of Script which allows it to be spawned and managed by a Manager instance. 13 | * 14 | * It provides extra handling for including other scripts, which are also loaded into the owning manager. 15 | * */ 16 | 17 | class ManagedScript extends Script { 18 | _manager = null; 19 | 20 | constructor( manager, filename, parent ) { 21 | super( filename, parent ); 22 | 23 | this._manager = manager; 24 | 25 | //When a script is renamed, it should be reassigned in the manager 26 | //Otherwise, when it's accessed at the new location, the manager just creates a new script 27 | this.on( 'rename', ( event, oldname, newname ) => { 28 | let {scripts} = this._manager; 29 | 30 | scripts.set( newname, scripts.get( oldname ) ); 31 | scripts.delete( oldname ); 32 | } ); 33 | } 34 | 35 | get manager() { 36 | return this._manager; 37 | } 38 | 39 | include( filename, load = false ) { 40 | //make sure filename can be relative to the current script 41 | var real_filename = resolve( this.baseUrl, filename ); 42 | 43 | //Since add doesn't do anything to already existing scripts, but does return a script, 44 | //it can take care of the lookup or adding at the same time. Two birds with one lookup. 45 | var script = this._manager.add( real_filename ); 46 | 47 | //Since include can be used independently of reference, make sure it's loaded before returning 48 | //Otherwise, the returned script is in an incomplete state 49 | if( load && !script.loaded ) { 50 | script.reload(); 51 | } 52 | 53 | this.propagateFrom( script, 'change', () => { 54 | this.unload(); 55 | this.emit( 'change', this.filename ); 56 | } ); 57 | 58 | script.propagateEvents( this.isPropagatingEvents() ); 59 | 60 | return script; 61 | } 62 | 63 | close( permanent ) { 64 | if( permanent ) { 65 | this._manager.scripts.delete( this.filename ); 66 | 67 | delete this['_manager']; 68 | } 69 | 70 | return super.close( permanent ); 71 | } 72 | } 73 | 74 | /* 75 | * This Manager class really just takes care of a Map instance of ManagedScripts and allows configuring them all at once 76 | * and automatically. 77 | * */ 78 | 79 | export default class Manager { 80 | _debounceMaxWait = null; 81 | _maxListeners = null; 82 | 83 | _config = null; 84 | _cwd = process.cwd(); 85 | 86 | _scripts = new Map(); 87 | _parent = null; 88 | 89 | _propagateEvents = false; 90 | 91 | _unloadOnRename = null; 92 | 93 | constructor( grandParent ) { 94 | this._parent = new Module( 'ScriptManager', grandParent ); 95 | } 96 | 97 | get parent() { 98 | return this._parent; 99 | } 100 | 101 | get scripts() { 102 | return this._scripts; 103 | } 104 | 105 | get debounceMaxWait() { 106 | return this._debounceMaxWait; 107 | } 108 | 109 | set debounceMaxWait( time ) { 110 | if( time !== null && time !== void 0 ) { 111 | time = Math.floor( time ); 112 | 113 | assert( !isNaN( time ), 'debounceMaxWait must be set to a number' ); 114 | 115 | this._debounceMaxWait = time; 116 | 117 | } else { 118 | this._debounceMaxWait = null; 119 | } 120 | } 121 | 122 | set unloadOnRename( value ) { 123 | this._unloadOnRename = !!value; 124 | } 125 | 126 | get unloadOnRename() { 127 | return this._unloadOnRename; 128 | } 129 | 130 | cwd() { 131 | return this._cwd; 132 | } 133 | 134 | chdir( value ) { 135 | this._cwd = resolve( this.cwd(), value ); 136 | 137 | return this._cwd; 138 | } 139 | 140 | setMaxListeners( value ) { 141 | if( value !== null && value !== void 0 ) { 142 | 143 | value = Math.floor( value ); 144 | 145 | assert( !isNaN( value ), 'setMaxListeners must be passed a number' ); 146 | 147 | this._maxListeners = value; 148 | 149 | } else { 150 | this._maxListeners = null; 151 | } 152 | } 153 | 154 | getMaxListeners() { 155 | return this._maxListeners; 156 | } 157 | 158 | config( config ) { 159 | this._config = normalizeConfig( config ); 160 | 161 | this._scripts.forEach( script => { 162 | script.config( this._config, true ); 163 | script.unload(); 164 | } ); 165 | } 166 | 167 | propagateEvents( enable = true ) { 168 | var wasPropagating = this._propagateEvents; 169 | 170 | this._propagateEvents = enable; 171 | 172 | if( wasPropagating && !enable ) { 173 | //immediately disable propagation by pretending it's already been propagated 174 | this._scripts.forEach( script => { 175 | script.propagateEvents( false ); 176 | } ); 177 | 178 | } else if( !wasPropagating && enable ) { 179 | this._scripts.forEach( script => { 180 | script.propagateEvents( true ); 181 | } ); 182 | } 183 | } 184 | 185 | _modifyScript( script ) { 186 | if( script !== void 0 ) { 187 | if( this._propagateEvents ) { 188 | script.propagateEvents(); 189 | } 190 | 191 | if( this.debounceMaxWait !== null && this.debounceMaxWait !== void 0 ) { 192 | script.debounceMaxWait = this.debounceMaxWait; 193 | } 194 | 195 | if( this._maxListeners !== null && this._maxListeners !== void 0 ) { 196 | script.setMaxListeners( this._maxListeners ); 197 | } 198 | 199 | if( this._config !== null && this._config !== void 0 ) { 200 | script.config( this._config, true ); 201 | } 202 | 203 | if( this._unloadOnRename !== null && this._unloadOnRename !== void 0 ) { 204 | script.unloadOnRename = this._unloadOnRename; 205 | } 206 | } 207 | 208 | return script; 209 | } 210 | 211 | /* 212 | * this and Script.watch are basically no-ops if nothing is to be added or it's already being watched 213 | * but this functions as a way to add and/or get a script in one fell swoop. 214 | * Since evaluation of a script is lazy, watch is defaulted to true, since there is almost no performance hit 215 | * from watching a file. 216 | * */ 217 | add( filename, watch = true ) { 218 | filename = resolve( this.cwd(), filename ); 219 | 220 | var script = this._scripts.get( filename ); 221 | 222 | if( script === void 0 ) { 223 | script = new ManagedScript( this, null, this._parent ); 224 | 225 | script.load( filename, watch ); 226 | 227 | this._scripts.set( filename, script ); 228 | } 229 | 230 | this._modifyScript( script ); 231 | 232 | //Even if the script is added, this allows it to be watched, though not unwatched. 233 | //Unwatching still has to be done manually 234 | if( watch ) { 235 | script.watch(); 236 | } 237 | 238 | return script; 239 | } 240 | 241 | /* 242 | * Removes a script from the manager. But closing it permenantly is optional, 243 | * as it may sometimes make sense to move it out of a manager and use it independently. 244 | * However, that is quite rare so close defaults to true 245 | * */ 246 | remove( filename, close = true ) { 247 | filename = resolve( this.cwd(), filename ); 248 | 249 | var script = this._scripts.get( filename ); 250 | 251 | if( script !== void 0 ) { 252 | if( close ) { 253 | script.close(); 254 | } 255 | 256 | return this._scripts.delete( filename ); 257 | } 258 | 259 | return false; 260 | } 261 | 262 | call( filename, ...args ) { 263 | return this.apply( filename, args ); 264 | } 265 | 266 | apply( filename, args ) { 267 | assert( Array.isArray( args ) ); 268 | 269 | var script = this.add( filename, false ); 270 | 271 | try { 272 | return script.apply( args ); 273 | 274 | } catch( err ) { 275 | this.remove( filename, true ); 276 | 277 | throw err; 278 | } 279 | } 280 | 281 | get( filename ) { 282 | filename = resolve( this.cwd(), filename ); 283 | 284 | return this._modifyScript( this._scripts.get( filename ) ); 285 | } 286 | 287 | //Make closing optional for the same reason as .remove 288 | clear( close = true ) { 289 | if( close ) { 290 | this._scripts.forEach( script => { 291 | script.close(); 292 | } ); 293 | } 294 | 295 | this._scripts.clear(); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/source_script.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/5/2015. 3 | */ 4 | 5 | import Promise from "bluebird"; 6 | import {basename, dirname} from "path"; 7 | import {stripBOM, injectAMD, tryPromise} from "./utils.js"; 8 | import Script from "./script.js"; 9 | 10 | /* 11 | * The SourceScript variation is a Script that allows loading from in-memory strings. These are always assumed to be normal JavaScript. 12 | * */ 13 | 14 | export function compile( src, watch = true, parent = null ) { 15 | var script = new SourceScript( src, parent ); 16 | 17 | if( watch ) { 18 | script.watch(); 19 | } 20 | 21 | return script; 22 | } 23 | 24 | export default class SourceScript extends Script { 25 | _onChange = null; 26 | 27 | constructor( src, parent = module ) { 28 | super( null, parent ); 29 | 30 | if( src !== void 0 && src !== null ) { 31 | this.load( src ); 32 | } 33 | } 34 | 35 | get filename() { 36 | return this._script.filename; 37 | } 38 | 39 | set filename( value ) { 40 | this._script.filename = value; 41 | } 42 | 43 | get baseUrl() { 44 | return dirname( this.filename ); 45 | } 46 | 47 | set baseUrl( value ) { 48 | value = dirname( value ); 49 | 50 | this.filename = value + basename( this.filename ); 51 | } 52 | 53 | get watched() { 54 | return typeof this._onChange === 'function'; 55 | } 56 | 57 | _do_load() { 58 | if( !this.loading || (this._loadingText && !this.textMode) ) { 59 | this.unload(); 60 | 61 | if( !this.textMode ) { 62 | this._do_setup(); 63 | 64 | this._loading = true; 65 | this._loadingText = false; 66 | 67 | if( this._willWatch ) { 68 | try { 69 | this._do_watch( this._watchPersistent ); 70 | 71 | } catch( err ) { 72 | this._loading = false; 73 | 74 | this.emit( 'loading_error', err ); 75 | } 76 | } 77 | 78 | this.source( 'utf-8' ).then( src => { 79 | this._script._compile( src, this.filename ); 80 | 81 | this._script.loaded = true; 82 | 83 | this._loading = false; 84 | 85 | this.emit( 'loaded', this._script.exports ); 86 | 87 | }, err => { 88 | this._loading = false; 89 | 90 | this.emit( 'loading_error', err ); 91 | } ); 92 | 93 | } else { 94 | this._loading = true; 95 | this._loadingText = true; 96 | 97 | if( this._willWatch ) { 98 | try { 99 | this._do_watch( this._watchPersistent ); 100 | 101 | } catch( err ) { 102 | this._loading = false; 103 | this._loadingText = false; 104 | 105 | this.emit( 'loading_src_error', err ); 106 | } 107 | } 108 | 109 | this.source( 'utf-8' ).then( src => { 110 | this._script.loaded = true; 111 | 112 | this._loading = false; 113 | this._loadingText = false; 114 | 115 | this.emit( 'loaded', this.loaded ); 116 | 117 | }, err => { 118 | this._loading = false; 119 | this._loadingText = false; 120 | 121 | this.emit( 'loading_error', err ); 122 | } ); 123 | } 124 | } 125 | } 126 | 127 | _normalizeSource( src ) { 128 | assert( typeof src === 'string' || Buffer.isBuffer( src ), 129 | 'Factory source must return string or Buffer as value' ); 130 | 131 | src = stripBOM( src ); 132 | 133 | if( !this.textMode && Script.extensions_enabled ) { 134 | src = injectAMD( src ); 135 | } 136 | 137 | if( Buffer.isBuffer( src ) && typeof encoding === 'string' ) { 138 | src = src.toString( encoding ); 139 | } 140 | 141 | return src; 142 | } 143 | 144 | source( encoding ) { 145 | if( typeof this._source === 'function' ) { 146 | return tryPromise( this._source() ).then( src => this._normalizeSource( src ) ); 147 | 148 | } else { 149 | try { 150 | let src = this._normalizeSource( this._source ); 151 | 152 | return Promise.resolve( src ); 153 | 154 | } catch( err ) { 155 | return Promise.reject( err ); 156 | } 157 | } 158 | } 159 | 160 | load( src, watch = true ) { 161 | assert( typeof src === 'string' || 162 | Buffer.isBuffer( src ) || 163 | typeof src === 'function', 'Source must be a string, Buffer or factory function' ); 164 | 165 | this.close( false ); 166 | 167 | this._source = typeof src === 'string' ? new Buffer( src ) : src; 168 | 169 | if( watch ) { 170 | this.watch(); 171 | } 172 | 173 | this.emit( 'change', 'change', this.filename ); 174 | 175 | return this; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/text_script.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/7/2015. 3 | */ 4 | 5 | import Script from "./script.js"; 6 | 7 | /* 8 | * This is just a variation on the normal script that forces it to always think it's in text mode 9 | * */ 10 | export default class TextScript extends Script { 11 | get textMode() { 12 | return true; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/4/2015. 3 | */ 4 | 5 | import Promise from "bluebird"; 6 | import * as _ from "lodash"; 7 | import {isAbsolute, resolve, posix as path} from "path"; 8 | import {AMD_Header, default_dependencies} from "./defaults.js"; 9 | 10 | let AMD_Header_Buffer = new Buffer( AMD_Header ); 11 | 12 | export var isAbsolutePath = typeof isAbsolute === 'function' ? isAbsolute : function isAbsolutePath( filepath ) { 13 | return normalize( filepath ) === resolve( filepath ); 14 | }; 15 | 16 | export function isAbsoluteOrRelative( filepath ) { 17 | return filepath.charAt( 0 ) === '.' || isAbsolutePath( filepath ); 18 | } 19 | 20 | export function bind( func, ...args ) { 21 | let bound = func.bind( ...args ); 22 | 23 | //This assumes sub-functions can handle their own scopes 24 | //or are closures that take that into account 25 | for( let it in func ) { 26 | if( func.hasOwnProperty( it ) ) { 27 | bound[it] = func[it]; 28 | } 29 | } 30 | 31 | return bound; 32 | } 33 | 34 | /* 35 | * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) 36 | * because the buffer-to-string conversion in `fs.readFileSync()` 37 | * translates it to FEFF, the UTF-16 BOM. 38 | * */ 39 | export function stripBOM( content ) { 40 | if( Buffer.isBuffer( content ) && content.length >= 2 && (content[0] === 0xFE && content[1] === 0xFF) ) { 41 | content = content.slice( 2 ); 42 | 43 | } else if( typeof content === 'string' && content.charCodeAt( 0 ) === 0xFEFF ) { 44 | content = content.slice( 1 ); 45 | } 46 | 47 | return content; 48 | } 49 | 50 | export function injectAMD( content ) { 51 | if( Buffer.isBuffer( content ) ) { 52 | return Buffer.concat( [AMD_Header_Buffer, content] ); 53 | 54 | } else if( typeof content === 'string' ) { 55 | return AMD_Header + content; 56 | 57 | } else { 58 | return content; 59 | } 60 | } 61 | 62 | export function injectAMDAndStripBOM( content ) { 63 | return injectAMD( stripBOM( content ) ); 64 | } 65 | 66 | export function parseDefine( id, deps, factory ) { 67 | //This argument parsing code is taken from amdefine 68 | if( Array.isArray( id ) ) { 69 | factory = deps; 70 | deps = id; 71 | id = void 0; 72 | 73 | } else if( typeof id !== 'string' ) { 74 | factory = id; 75 | id = deps = void 0; 76 | } 77 | 78 | if( deps !== void 0 && !Array.isArray( deps ) ) { 79 | factory = deps; 80 | deps = void 0; 81 | } 82 | 83 | if( deps === void 0 ) { 84 | deps = default_dependencies; 85 | 86 | } else { 87 | deps = deps.concat( default_dependencies ) 88 | } 89 | 90 | return [id, deps, factory]; 91 | } 92 | 93 | /* 94 | * Simple test to see if an object is a Promise instance 95 | * */ 96 | export function isThenable( obj ) { 97 | return obj !== void 0 && obj !== null && (obj instanceof Promise || typeof obj.then === 'function'); 98 | } 99 | 100 | /* 101 | * "coerces" a value into a Promise. 102 | * */ 103 | export function tryPromise( value ) { 104 | if( isThenable( value ) ) { 105 | return value; 106 | 107 | } else { 108 | return Promise.resolve( value ); 109 | } 110 | } 111 | 112 | /* 113 | * Executes a function synchronously and returns a rejected Promise if it throws 114 | * */ 115 | export function tryReject( func, context, ...args ) { 116 | try { 117 | return tryPromise( func.apply( context, args ) ); 118 | 119 | } catch( err ) { 120 | return Promise.reject( err ); 121 | } 122 | } 123 | 124 | /* 125 | * The next few do exactly what they say on the tin. 126 | * */ 127 | 128 | export function isGenerator( obj ) { 129 | return 'function' === typeof obj.next && 'function' === typeof obj.throw; 130 | } 131 | 132 | export function isGeneratorFunction( obj ) { 133 | if( !obj.constructor ) { 134 | return false; 135 | 136 | } else if( 'GeneratorFunction' === obj.constructor.name || 137 | 'GeneratorFunction' === obj.constructor.displayName ) { 138 | return true; 139 | 140 | } else { 141 | return isGenerator( obj.constructor.prototype ); 142 | } 143 | } 144 | 145 | function isNull( value ) { 146 | return value === null || value === void 0; 147 | } 148 | 149 | function toPosix( filepath ) { 150 | return filepath.replace( '\\', '/' ); 151 | } 152 | 153 | export function parseDeps( deps, paths ) { 154 | if( _.isObject( deps ) && !Array.isArray( deps ) ) { 155 | return _.map( deps, function( v, k ) { 156 | 157 | //If deps have a specified path, use that instead, but only if it hasn't already been defined 158 | if( !paths[k] ) { 159 | paths[k] = v; 160 | } 161 | 162 | return k; 163 | } ); 164 | 165 | } else if( !Array.isArray( deps ) ) { 166 | return [/*No valid dependencies*/]; 167 | 168 | } else { 169 | return deps; 170 | } 171 | } 172 | 173 | /* 174 | * This is pretty complicated. 175 | * 176 | * TODO: Explain this. 177 | * */ 178 | export function normalizeConfig( config ) { 179 | var isObject = _.isObject( config ) && !Array.isArray( config ); 180 | 181 | var defaultConfig = { 182 | baseUrl: '.' + path.sep, //String 183 | paths: {}, //Object 184 | deps: [], //Array 185 | shim: {} //Object 186 | }; 187 | 188 | if( isObject ) { 189 | config = _.defaults( config, defaultConfig ); 190 | 191 | } else { 192 | return defaultConfig; 193 | } 194 | 195 | //Normalize baseUrl 196 | if( typeof config.baseUrl === 'string' ) { 197 | config.baseUrl = path.normalize( toPosix( config.baseUrl ) ); 198 | 199 | } else { 200 | config.baseUrl = defaultConfig.baseUrl; 201 | } 202 | 203 | //Make sure paths is an object 204 | if( !_.isObject( config.paths ) || Array.isArray( config.paths ) ) { 205 | config.paths = defaultConfig.paths; 206 | } 207 | 208 | //Make sure shim is an object 209 | if( !_.isObject( config.shim ) || Array.isArray( config.shim ) ) { 210 | config.shim = defaultConfig.shim; 211 | } 212 | 213 | //Normalize deps 214 | config.deps = parseDeps( config.deps, config.paths ); 215 | 216 | //Normalize shims 217 | config.shim = _.chain( config.shim ).mapValues( function( shim ) { 218 | if( Array.isArray( shim ) ) { 219 | return { 220 | deps: parseDeps( shim, config.paths ) 221 | }; 222 | 223 | } else if( _.isObject( shim ) && typeof shim.exports === 'string' ) { 224 | return { 225 | deps: parseDeps( shim.deps, config.paths ), 226 | exports: shim.exports 227 | }; 228 | } 229 | 230 | } ).omit( isNull ).value(); 231 | 232 | //Normalize paths 233 | config.paths = _.chain( config.paths ).mapValues( function( p ) { 234 | if( typeof p === 'string' ) { 235 | return toPosix( p ); 236 | } 237 | 238 | } ).omit( isNull ).value(); 239 | 240 | return config; 241 | } 242 | -------------------------------------------------------------------------------- /test/build/0_basic.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _typeof2 = require( "babel-runtime/helpers/typeof" ); 4 | 5 | var _typeof3 = _interopRequireDefault( _typeof2 ); 6 | 7 | var _ = require( "../../" ); 8 | 9 | var _2 = _interopRequireDefault( _ ); 10 | 11 | var _assert = require( "assert" ); 12 | 13 | var _assert2 = _interopRequireDefault( _assert ); 14 | 15 | var _bluebird = require( "bluebird" ); 16 | 17 | var _bluebird2 = _interopRequireDefault( _bluebird ); 18 | 19 | function _interopRequireDefault( obj ) { 20 | return obj && obj.__esModule ? obj : {default: obj}; 21 | } 22 | 23 | describe( "exports", function() { 24 | it( 'should have exported a Script class', function() { 25 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.Script ), 'function', 'No Script class exported' ); 26 | } ); 27 | 28 | it( 'should have exported a SourceScript class', function() { 29 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.SourceScript ), 'function', 'No SourceScript class exported' ); 30 | } ); 31 | 32 | it( 'should have exported a TextScript class', function() { 33 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.TextScript ), 'function', 'No TextScript class exported' ); 34 | } ); 35 | 36 | it( 'should have exported a compile function', function() { 37 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.compile ), 'function', 'No compile function exported' ); 38 | } ); 39 | 40 | it( 'should have exported a load function', function() { 41 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.load ), 'function', 'No load function exported' ); 42 | } ); 43 | 44 | it( 'should have exported a Manager class', function() { 45 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.Manager ), 'function', 'No Manager class exported' ); 46 | } ); 47 | 48 | it( 'should have exported a bluebird Promise class', function() { 49 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.Promise ), 'function', 'No Promise class exported' ); 50 | _assert2.default.strictEqual( _2.default.Promise, _bluebird2.default, 'Exported Promise is not bluebird' ); 51 | } ); 52 | 53 | it( 'should have exported a few utility functions', function() { 54 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.utils ), 'object', 'No utilities exported' ); 55 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.utils.stripBOM ), 'function', 'Missing utility "stripBOM"' ); 56 | _assert2.default.strictEqual( (0, _typeof3.default)( _2.default.utils.injectAMD ), 'function', 'Missing utility "stripBOM"' ); 57 | } ); 58 | } ); 59 | /** 60 | * Created by Aaron on 7/5/2015. 61 | */ 62 | -------------------------------------------------------------------------------- /test/build/1_script_init.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require( "../../" ); 4 | 5 | var _2 = _interopRequireDefault( _ ); 6 | 7 | var _assert = require( "assert" ); 8 | 9 | var _assert2 = _interopRequireDefault( _assert ); 10 | 11 | function _interopRequireDefault( obj ) { 12 | return obj && obj.__esModule ? obj : {default: obj}; 13 | } 14 | 15 | /** 16 | * Created by Aaron on 7/6/2015. 17 | */ 18 | 19 | var Module = require( 'module' ); 20 | 21 | describe( "Script initialization", function() { 22 | var Script = _2.default.Script; 23 | 24 | describe( 'Creating a new Script without default filename', function() { 25 | var script; 26 | 27 | it( 'should create a new Script instance', function() { 28 | script = new Script( module ); 29 | 30 | (0, _assert2.default)( script instanceof Script ); 31 | } ); 32 | 33 | it( 'should have created a new Module instance internally', function() { 34 | (0, _assert2.default)( script._script instanceof Module ); 35 | } ); 36 | 37 | it( 'should use provided module as parent', function() { 38 | _assert2.default.strictEqual( script.parent, module ); 39 | } ); 40 | 41 | it( 'should not be loaded', function() { 42 | (0, _assert2.default)( !script.loaded ); 43 | } ); 44 | 45 | it( 'should NOT be watching a file', function() { 46 | (0, _assert2.default)( !script.watched ); 47 | } ); 48 | 49 | it( 'should NOT be ready to watch the file upon load', function() { 50 | (0, _assert2.default)( !script.willWatch ); 51 | } ); 52 | } ); 53 | 54 | describe( 'Creating a new Script with filename and module', function() { 55 | var script, 56 | name = './test/fixtures/empty.js'; 57 | 58 | it( 'should create a new Script instance', function() { 59 | script = new Script( name, module ); 60 | 61 | (0, _assert2.default)( script instanceof Script ); 62 | } ); 63 | 64 | it( 'should have created a new Module instance internally', function() { 65 | (0, _assert2.default)( script._script instanceof Module ); 66 | } ); 67 | 68 | it( 'should use provided module as parent', function() { 69 | _assert2.default.strictEqual( script.parent, module ); 70 | } ); 71 | 72 | it( 'should not be loaded', function() { 73 | (0, _assert2.default)( !script.loaded ); 74 | } ); 75 | 76 | it( 'should NOT be watching the file', function() { 77 | (0, _assert2.default)( !script.watched ); 78 | } ); 79 | 80 | it( 'should be ready to watch the file upon load', function() { 81 | (0, _assert2.default)( script.willWatch ); 82 | } ); 83 | } ); 84 | } ); 85 | -------------------------------------------------------------------------------- /test/build/2_script_loading.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require( "../../" ); 4 | 5 | var _2 = _interopRequireDefault( _ ); 6 | 7 | var _assert = require( "assert" ); 8 | 9 | var _assert2 = _interopRequireDefault( _assert ); 10 | 11 | function _interopRequireDefault( obj ) { 12 | return obj && obj.__esModule ? obj : {default: obj}; 13 | } 14 | 15 | /** 16 | * Created by Aaron on 7/6/2015. 17 | */ 18 | 19 | describe( "Script loading", function() { 20 | var Script = _2.default.Script; 21 | 22 | describe( 'empty file', function() { 23 | var script = new Script( './test/fixtures/empty.js', module ); 24 | 25 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 26 | script.exports().then( function( script_exports ) { 27 | _assert2.default.deepEqual( script_exports, {} ); 28 | (0, _assert2.default)( script.loaded ); 29 | } ).then( done, done ); 30 | } ); 31 | } ); 32 | 33 | describe( 'simple script with CommonJS style exports', function() { 34 | var script = new Script( './test/fixtures/loading/commonjs_simple.js', module ); 35 | 36 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 37 | script.exports().then( function( script_exports ) { 38 | _assert2.default.deepEqual( script_exports, { 39 | test: 42 40 | } ); 41 | (0, _assert2.default)( script.loaded ); 42 | } ).then( done, done ); 43 | } ); 44 | } ); 45 | 46 | describe( 'simple script with simple AMD style factory exports', function() { 47 | var script = new Script( './test/fixtures/loading/amd_simple.js', module ); 48 | 49 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 50 | script.exports().then( function( script_exports ) { 51 | _assert2.default.deepEqual( script_exports, { 52 | test: 42 53 | } ); 54 | (0, _assert2.default)( script.loaded ); 55 | } ).then( done, done ); 56 | } ); 57 | 58 | it( 'should be watching the file after load', function() { 59 | (0, _assert2.default)( script.watched ); 60 | } ); 61 | } ); 62 | 63 | describe( 'simple script with AMD strict style factory exports', function() { 64 | var script = new Script( './test/fixtures/loading/amd_strict.js', module ); 65 | 66 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 67 | script.exports().then( function( script_exports ) { 68 | _assert2.default.deepEqual( script_exports, { 69 | 'default': { 70 | test: 42 71 | }, 72 | __esModule: true 73 | } ); 74 | (0, _assert2.default)( script.loaded ); 75 | } ).then( done, done ); 76 | } ); 77 | 78 | it( 'should be watching the file after load', function() { 79 | (0, _assert2.default)( script.watched ); 80 | } ); 81 | } ); 82 | } ); 83 | -------------------------------------------------------------------------------- /test/build/3_script_calling.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require( "../../" ); 4 | 5 | var _2 = _interopRequireDefault( _ ); 6 | 7 | var _assert = require( "assert" ); 8 | 9 | var _assert2 = _interopRequireDefault( _assert ); 10 | 11 | function _interopRequireDefault( obj ) { 12 | return obj && obj.__esModule ? obj : {default: obj}; 13 | } 14 | 15 | /** 16 | * Created by Aaron on 7/6/2015. 17 | */ 18 | 19 | var Module = require( 'module' ); 20 | 21 | describe( "Script calling", function() { 22 | var Script = _2.default.Script; 23 | 24 | describe( 'empty file', function() { 25 | var script = new Script( './test/fixtures/empty.js', module ); 26 | 27 | it( 'should not execute anything but just return the empty exports', function( done ) { 28 | script.call().then( function( result ) { 29 | _assert2.default.deepEqual( result, {} ); 30 | (0, _assert2.default)( script.loaded ); 31 | } ).then( done ); 32 | } ); 33 | } ); 34 | 35 | describe( 'simple module.exports = function(){...}', function() { 36 | var script = new Script( './test/fixtures/calling/simple.js', module ); 37 | 38 | it( 'should call the exported function and return the result', function( done ) { 39 | script.call().then( function( result ) { 40 | _assert2.default.deepEqual( result, { 41 | towel: "Don't Forget Yours" 42 | } ); 43 | (0, _assert2.default)( script.loaded ); 44 | } ).then( done ); 45 | } ); 46 | } ); 47 | 48 | describe( 'coroutine as main function', function() { 49 | var script = new Script( './test/fixtures/calling/coroutine.js', module ); 50 | 51 | it( 'should call the exported function and return the result', function( done ) { 52 | script.call().then( function( result ) { 53 | _assert2.default.deepEqual( result, 42 ); 54 | (0, _assert2.default)( script.loaded ); 55 | } ).then( done ); 56 | } ); 57 | } ); 58 | 59 | describe( 'passing arguments to main function', function() { 60 | var script = new Script( './test/fixtures/calling/arguments.js', module ); 61 | 62 | it( 'should call the exported function and return the result', function( done ) { 63 | script.call( 'test' ).then( function( result ) { 64 | _assert2.default.deepEqual( result, 'Hello' ); 65 | 66 | (0, _assert2.default)( script.loaded ); 67 | } ).then( done ); 68 | } ); 69 | } ); 70 | } ); 71 | -------------------------------------------------------------------------------- /test/build/4_script_watching.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require( "../../" ); 4 | 5 | var _2 = _interopRequireDefault( _ ); 6 | 7 | var _assert = require( "assert" ); 8 | 9 | var _assert2 = _interopRequireDefault( _assert ); 10 | 11 | function _interopRequireDefault( obj ) { 12 | return obj && obj.__esModule ? obj : {default: obj}; 13 | } 14 | 15 | /** 16 | * Created by Aaron on 7/6/2015. 17 | */ 18 | 19 | var fs = require( 'fs' ); 20 | var touch = require( 'touch' ); 21 | 22 | describe( "Script watching", function() { 23 | var Script = _2.default.Script; 24 | 25 | describe( 'simple script', function() { 26 | var script = new Script( './test/fixtures/watching/simple.js', module ); 27 | 28 | it( 'should NOT be watching the file before load', function() { 29 | (0, _assert2.default)( !script.watched ); 30 | } ); 31 | 32 | it( 'should watch the file upon load', function() { 33 | (0, _assert2.default)( script.willWatch ); 34 | } ); 35 | 36 | it( 'should call the exported function and return the result', function( done ) { 37 | script.exports().then( function( script_exports ) { 38 | _assert2.default.deepEqual( script_exports, { 39 | test: 42 40 | } ); 41 | (0, _assert2.default)( script.loaded ); 42 | } ).then( done ); 43 | } ); 44 | 45 | it( 'should be watching the file after load', function() { 46 | (0, _assert2.default)( script.watched ); 47 | } ); 48 | 49 | it( 'should trigger the change event when the file is modified', function( done ) { 50 | script.once( 'change', function() { 51 | done(); 52 | } ); 53 | 54 | touch( script.filename ); 55 | } ); 56 | 57 | it( 'should be unloaded after the change', function() { 58 | (0, _assert2.default)( !script.loaded ); 59 | } ); 60 | 61 | it( 'should be able to reload the script with the changes implicitly', function( done ) { 62 | script.exports().then( function( script_exports ) { 63 | _assert2.default.deepEqual( script_exports, { 64 | test: 42 65 | } ); 66 | (0, _assert2.default)( script.loaded ); 67 | } ).then( done ); 68 | } ); 69 | 70 | it( 'should be able to unwatch a file', function() { 71 | script.unwatch(); 72 | 73 | (0, _assert2.default)( !script.watched ); 74 | } ); 75 | 76 | it( 'should not unload if the file is changed when the script is not watched', function( done ) { 77 | var watcher = fs.watch( script.filename, function() { 78 | (0, _assert2.default)( script.loaded ); 79 | watcher.close(); 80 | done(); 81 | } ); 82 | 83 | touch( script.filename ); 84 | } ); 85 | } ); 86 | } ); 87 | -------------------------------------------------------------------------------- /test/build/5_amd_scripts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require( "../../" ); 4 | 5 | var _2 = _interopRequireDefault( _ ); 6 | 7 | var _assert = require( "assert" ); 8 | 9 | var _assert2 = _interopRequireDefault( _assert ); 10 | 11 | function _interopRequireDefault( obj ) { 12 | return obj && obj.__esModule ? obj : {default: obj}; 13 | } 14 | 15 | /** 16 | * Created by Aaron on 7/6/2015. 17 | */ 18 | 19 | describe( "AMD Scripts", function() { 20 | var Script = _2.default.Script; 21 | 22 | describe( 'simple script that requires a build-in module', function() { 23 | var script = new Script( './test/fixtures/amd/simple.js', module ); 24 | 25 | it( 'should load without any issues', function( done ) { 26 | script.exports().then( function() { 27 | (0, _assert2.default)( script.loaded ); 28 | } ).then( done ); 29 | } ); 30 | } ); 31 | 32 | describe( 'using built-in plugins', function() { 33 | var script = new Script( './test/fixtures/amd/builtin_plugins.js', module ); 34 | 35 | it( 'should load without any issues', function( done ) { 36 | script.exports().then( function() { 37 | (0, _assert2.default)( script.loaded ); 38 | } ).then( done ); 39 | } ); 40 | } ); 41 | } ); 42 | -------------------------------------------------------------------------------- /test/build/6_text_scripts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require( "../../" ); 4 | 5 | var _2 = _interopRequireDefault( _ ); 6 | 7 | var _assert = require( "assert" ); 8 | 9 | var _assert2 = _interopRequireDefault( _assert ); 10 | 11 | function _interopRequireDefault( obj ) { 12 | return obj && obj.__esModule ? obj : {default: obj}; 13 | } 14 | 15 | /** 16 | * Created by Aaron on 7/7/2015. 17 | */ 18 | 19 | describe( "TextScripts", function() { 20 | var TextScript = _2.default.TextScript; 21 | 22 | describe( 'simple file with hello world', function() { 23 | var script = new TextScript( './test/fixtures/text/hello.txt', module ); 24 | 25 | it( 'should not export anything', function( done ) { 26 | script.exports().then( function( script_exports ) { 27 | _assert2.default.deepEqual( script_exports, {} ); 28 | (0, _assert2.default)( script.loaded ); 29 | } ).then( done ); 30 | } ); 31 | 32 | it( 'should give the text as the script source', function( done ) { 33 | script.source( 'utf-8' ).then( function( src ) { 34 | _assert2.default.strictEqual( src.trim(), 'Hello, World!' ); 35 | } ).then( done ); 36 | } ); 37 | } ); 38 | } ); 39 | -------------------------------------------------------------------------------- /test/build/7_amd_errors.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _ = require( "../../" ); 4 | 5 | var _2 = _interopRequireDefault( _ ); 6 | 7 | var _assert = require( "assert" ); 8 | 9 | var _assert2 = _interopRequireDefault( _assert ); 10 | 11 | function _interopRequireDefault( obj ) { 12 | return obj && obj.__esModule ? obj : {default: obj}; 13 | } 14 | 15 | /** 16 | * Created by Aaron on 7/7/2015. 17 | */ 18 | 19 | describe( "Script errors", function() { 20 | var Script = _2.default.Script; 21 | 22 | describe( 'simple script that throws an error the first time define is invoked', function() { 23 | var script = new Script( './test/fixtures/amd/error.js', module ); 24 | 25 | it( 'should throw an error the first time', function( done ) { 26 | var hadError = false; 27 | 28 | script.exports().catch( function( err ) { 29 | (0, _assert2.default)( err instanceof Error ); 30 | _assert2.default.deepEqual( script._script.exports, {} ); 31 | (0, _assert2.default)( script.loaded ); 32 | (0, _assert2.default)( script.pending ); 33 | 34 | hadError = true; 35 | } ).then( function() { 36 | (0, _assert2.default)( hadError, 'No error was thrown' ); 37 | } ).then( done, done ); 38 | } ); 39 | 40 | it( 'should succeed and finish loading the second time', function( done ) { 41 | script.exports().then( function( script_exports ) { 42 | _assert2.default.deepEqual( script_exports, { 43 | test: 42 44 | } ); 45 | (0, _assert2.default)( script.loaded ); 46 | (0, _assert2.default)( !script.pending ); 47 | } ).then( done ); 48 | } ); 49 | } ); 50 | } ); 51 | -------------------------------------------------------------------------------- /test/build/8_manager.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _map = require( "babel-runtime/core-js/map" ); 4 | 5 | var _map2 = _interopRequireDefault( _map ); 6 | 7 | var _ = require( "../../" ); 8 | 9 | var _2 = _interopRequireDefault( _ ); 10 | 11 | var _module = require( "module" ); 12 | 13 | var _module2 = _interopRequireDefault( _module ); 14 | 15 | var _path = require( "path" ); 16 | 17 | var _assert = require( "assert" ); 18 | 19 | var _assert2 = _interopRequireDefault( _assert ); 20 | 21 | function _interopRequireDefault( obj ) { 22 | return obj && obj.__esModule ? obj : {default: obj}; 23 | } 24 | 25 | /** 26 | * Created by Aaron on 7/7/2015. 27 | */ 28 | 29 | describe( "Managers", function() { 30 | var Manager = _2.default.Manager; 31 | var Script = _2.default.Script; 32 | 33 | 34 | var manager = void 0; 35 | 36 | describe( 'Creating a manager', function() { 37 | manager = new Manager( module ); 38 | 39 | it( 'should result in an empty manager with a parent module', function() { 40 | (0, _assert2.default)( manager.parent instanceof _module2.default ); 41 | _assert2.default.strictEqual( manager.parent.parent, module ); 42 | (0, _assert2.default)( manager.scripts instanceof _map2.default ); 43 | _assert2.default.strictEqual( manager.scripts.size, 0 ); 44 | } ); 45 | 46 | it( 'should set cwd to process.cwd', function() { 47 | _assert2.default.strictEqual( manager.cwd(), process.cwd() ); 48 | } ); 49 | 50 | it( 'should allow changing cwd', function() { 51 | manager.chdir( './fixtures' ); 52 | 53 | _assert2.default.strictEqual( manager.cwd(), (0, _path.resolve)( process.cwd(), './fixtures' ) ); 54 | } ); 55 | } ); 56 | 57 | describe( 'Add simple scripts through a manager instance', function() { 58 | it( 'should return the Script instance when passed a path relative to the cwd', function() { 59 | var script = manager.add( 'empty.js' ); 60 | 61 | (0, _assert2.default)( script instanceof Script ); 62 | _assert2.default.strictEqual( script.filename, (0, _path.resolve)( process.cwd(), './fixtures/empty.js' ) ); 63 | } ); 64 | } ); 65 | } ); 66 | -------------------------------------------------------------------------------- /test/fixtures/amd/builtin_plugins.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | define( ['assert', 'promisify!crypto', 'crypto'], function*( assert, cryptoAsync, crypto ) { 6 | assert.notStrictEqual( cryptoAsync, crypto ); 7 | assert.strictEqual( cryptoAsync.randomBytes, crypto.randomBytes ); 8 | assert.strictEqual( typeof cryptoAsync.randomBytesAsync, 'function' ); 9 | 10 | var k = yield cryptoAsync.randomBytesAsync( 10 ); 11 | 12 | assert( Buffer.isBuffer( k ) ); 13 | assert.strictEqual( k.length, 10 ); 14 | } ); 15 | -------------------------------------------------------------------------------- /test/fixtures/amd/error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/7/2015. 3 | */ 4 | 5 | var value = 1; 6 | 7 | define( function() { 8 | if( value++ === 1 ) { 9 | throw new Error( 'fail' ); 10 | } 11 | 12 | return { 13 | test: 42 14 | }; 15 | } ); 16 | -------------------------------------------------------------------------------- /test/fixtures/amd/simple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | define( ['assert'], function( assert ) { 6 | assert( true ); 7 | } ); 8 | -------------------------------------------------------------------------------- /test/fixtures/calling/arguments.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | module.exports = function( value ) { 6 | return value === 'test' ? 'Hello' : null; 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/calling/coroutine.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | var Promise = require( 'bluebird' ); 6 | 7 | module.exports = function*() { 8 | return yield new Promise.delay( 10 ).return( 42 ); 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/calling/simple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | module.exports = function() { 6 | return { 7 | towel: "Don't Forget Yours" 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/empty.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | -------------------------------------------------------------------------------- /test/fixtures/loading/amd_simple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | define( function() { 6 | return { 7 | test: 42 8 | }; 9 | } ); 10 | -------------------------------------------------------------------------------- /test/fixtures/loading/amd_strict.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | define( ['exports'], function( exports ) { 6 | exports['default'] = { 7 | test: 42 8 | }; 9 | 10 | exports.__esModule = true; 11 | } ); 12 | -------------------------------------------------------------------------------- /test/fixtures/loading/commonjs_simple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | module.exports = { 6 | test: 42 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/text/hello.txt: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /test/fixtures/watching/simple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | module.exports = { 6 | test: 42 7 | }; 8 | -------------------------------------------------------------------------------- /test/src/0_basic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/5/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import assert from "assert"; 7 | import Promise from "bluebird"; 8 | 9 | 10 | describe( `exports`, function() { 11 | it( 'should have exported a Script class', function() { 12 | assert.strictEqual( typeof Scriptor.Script, 'function', 'No Script class exported' ); 13 | } ); 14 | 15 | it( 'should have exported a SourceScript class', function() { 16 | assert.strictEqual( typeof Scriptor.SourceScript, 'function', 'No SourceScript class exported' ); 17 | } ); 18 | 19 | it( 'should have exported a TextScript class', function() { 20 | assert.strictEqual( typeof Scriptor.TextScript, 'function', 'No TextScript class exported' ); 21 | } ); 22 | 23 | it( 'should have exported a compile function', function() { 24 | assert.strictEqual( typeof Scriptor.compile, 'function', 'No compile function exported' ); 25 | } ); 26 | 27 | it( 'should have exported a load function', function() { 28 | assert.strictEqual( typeof Scriptor.load, 'function', 'No load function exported' ); 29 | } ); 30 | 31 | it( 'should have exported a Manager class', function() { 32 | assert.strictEqual( typeof Scriptor.Manager, 'function', 'No Manager class exported' ); 33 | } ); 34 | 35 | it( 'should have exported a bluebird Promise class', function() { 36 | assert.strictEqual( typeof Scriptor.Promise, 'function', 'No Promise class exported' ); 37 | assert.strictEqual( Scriptor.Promise, Promise, 'Exported Promise is not bluebird' ); 38 | } ); 39 | 40 | it( 'should have exported a few utility functions', function() { 41 | assert.strictEqual( typeof Scriptor.utils, 'object', 'No utilities exported' ); 42 | assert.strictEqual( typeof Scriptor.utils.stripBOM, 'function', 'Missing utility "stripBOM"' ); 43 | assert.strictEqual( typeof Scriptor.utils.injectAMD, 'function', 'Missing utility "stripBOM"' ); 44 | } ); 45 | } ); -------------------------------------------------------------------------------- /test/src/1_script_init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | 6 | import Scriptor from "../../"; 7 | import assert from "assert"; 8 | 9 | var Module = require( 'module' ); 10 | 11 | 12 | describe( `Script initialization`, function() { 13 | let Script = Scriptor.Script; 14 | 15 | describe( 'Creating a new Script without default filename', function() { 16 | var script; 17 | 18 | it( 'should create a new Script instance', function() { 19 | script = new Script( module ); 20 | 21 | assert( script instanceof Script ); 22 | } ); 23 | 24 | it( 'should have created a new Module instance internally', function() { 25 | assert( script._script instanceof Module ); 26 | } ); 27 | 28 | it( 'should use provided module as parent', function() { 29 | assert.strictEqual( script.parent, module ); 30 | } ); 31 | 32 | it( 'should not be loaded', function() { 33 | assert( !script.loaded ); 34 | } ); 35 | 36 | it( 'should NOT be watching a file', function() { 37 | assert( !script.watched ); 38 | } ); 39 | 40 | it( 'should NOT be ready to watch the file upon load', function() { 41 | assert( !script.willWatch ); 42 | } ); 43 | } ); 44 | 45 | describe( 'Creating a new Script with filename and module', function() { 46 | var script, name = './test/fixtures/empty.js'; 47 | 48 | it( 'should create a new Script instance', function() { 49 | script = new Script( name, module ); 50 | 51 | assert( script instanceof Script ); 52 | } ); 53 | 54 | it( 'should have created a new Module instance internally', function() { 55 | assert( script._script instanceof Module ); 56 | } ); 57 | 58 | it( 'should use provided module as parent', function() { 59 | assert.strictEqual( script.parent, module ); 60 | } ); 61 | 62 | it( 'should not be loaded', function() { 63 | assert( !script.loaded ); 64 | } ); 65 | 66 | it( 'should NOT be watching the file', function() { 67 | assert( !script.watched ); 68 | } ); 69 | 70 | it( 'should be ready to watch the file upon load', function() { 71 | assert( script.willWatch ); 72 | } ); 73 | } ); 74 | } ); -------------------------------------------------------------------------------- /test/src/2_script_loading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import assert from "assert"; 7 | 8 | describe( `Script loading`, function() { 9 | let Script = Scriptor.Script; 10 | 11 | describe( 'empty file', function() { 12 | let script = new Script( './test/fixtures/empty.js', module ); 13 | 14 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 15 | script.exports().then( function( script_exports ) { 16 | assert.deepEqual( script_exports, {} ); 17 | assert( script.loaded ); 18 | 19 | } ).then( done, done ); 20 | } ); 21 | } ); 22 | 23 | describe( 'simple script with CommonJS style exports', function() { 24 | let script = new Script( './test/fixtures/loading/commonjs_simple.js', module ); 25 | 26 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 27 | script.exports().then( function( script_exports ) { 28 | assert.deepEqual( script_exports, { 29 | test: 42 30 | } ); 31 | assert( script.loaded ); 32 | 33 | } ).then( done, done ); 34 | } ); 35 | } ); 36 | 37 | describe( 'simple script with simple AMD style factory exports', function() { 38 | let script = new Script( './test/fixtures/loading/amd_simple.js', module ); 39 | 40 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 41 | script.exports().then( function( script_exports ) { 42 | assert.deepEqual( script_exports, { 43 | test: 42 44 | } ); 45 | assert( script.loaded ); 46 | 47 | } ).then( done, done ); 48 | } ); 49 | 50 | it( 'should be watching the file after load', function() { 51 | assert( script.watched ); 52 | } ); 53 | } ); 54 | 55 | describe( 'simple script with AMD strict style factory exports', function() { 56 | let script = new Script( './test/fixtures/loading/amd_strict.js', module ); 57 | 58 | it( 'should load the file upon calling it (lazy evaluation)', function( done ) { 59 | script.exports().then( function( script_exports ) { 60 | assert.deepEqual( script_exports, { 61 | 'default': { 62 | test: 42 63 | }, 64 | __esModule: true 65 | } ); 66 | assert( script.loaded ); 67 | 68 | } ).then( done, done ); 69 | } ); 70 | 71 | it( 'should be watching the file after load', function() { 72 | assert( script.watched ); 73 | } ); 74 | } ); 75 | } ); -------------------------------------------------------------------------------- /test/src/3_script_calling.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import assert from "assert"; 7 | 8 | var Module = require( 'module' ); 9 | 10 | describe( `Script calling`, function() { 11 | let Script = Scriptor.Script; 12 | 13 | describe( 'empty file', function() { 14 | let script = new Script( './test/fixtures/empty.js', module ); 15 | 16 | it( 'should not execute anything but just return the empty exports', function( done ) { 17 | script.call().then( function( result ) { 18 | assert.deepEqual( result, {} ); 19 | assert( script.loaded ); 20 | 21 | } ).then( done ); 22 | } ); 23 | } ); 24 | 25 | describe( 'simple module.exports = function(){...}', function() { 26 | let script = new Script( './test/fixtures/calling/simple.js', module ); 27 | 28 | it( 'should call the exported function and return the result', function( done ) { 29 | script.call().then( function( result ) { 30 | assert.deepEqual( result, { 31 | towel: "Don't Forget Yours" 32 | } ); 33 | assert( script.loaded ); 34 | 35 | } ).then( done ); 36 | } ); 37 | } ); 38 | 39 | describe( 'coroutine as main function', function() { 40 | let script = new Script( './test/fixtures/calling/coroutine.js', module ); 41 | 42 | it( 'should call the exported function and return the result', function( done ) { 43 | script.call().then( function( result ) { 44 | assert.deepEqual( result, 42 ); 45 | assert( script.loaded ); 46 | 47 | } ).then( done ); 48 | } ); 49 | } ); 50 | 51 | describe( 'passing arguments to main function', function() { 52 | let script = new Script( './test/fixtures/calling/arguments.js', module ); 53 | 54 | it( 'should call the exported function and return the result', function( done ) { 55 | script.call( 'test' ).then( function( result ) { 56 | assert.deepEqual( result, 'Hello' ); 57 | 58 | assert( script.loaded ); 59 | 60 | } ).then( done ); 61 | } ); 62 | } ); 63 | } ); -------------------------------------------------------------------------------- /test/src/4_script_watching.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import assert from "assert"; 7 | 8 | var fs = require( 'fs' ); 9 | var touch = require( 'touch' ); 10 | 11 | describe( `Script watching`, function() { 12 | let Script = Scriptor.Script; 13 | 14 | describe( 'simple script', function() { 15 | let script = new Script( './test/fixtures/watching/simple.js', module ); 16 | 17 | it( 'should NOT be watching the file before load', function() { 18 | assert( !script.watched ); 19 | } ); 20 | 21 | it( 'should watch the file upon load', function() { 22 | assert( script.willWatch ); 23 | } ); 24 | 25 | it( 'should call the exported function and return the result', function( done ) { 26 | script.exports().then( function( script_exports ) { 27 | assert.deepEqual( script_exports, { 28 | test: 42 29 | } ); 30 | assert( script.loaded ); 31 | 32 | } ).then( done ); 33 | } ); 34 | 35 | it( 'should be watching the file after load', function() { 36 | assert( script.watched ); 37 | } ); 38 | 39 | it( 'should trigger the change event when the file is modified', function( done ) { 40 | script.once( 'change', function() { 41 | done(); 42 | } ); 43 | 44 | touch( script.filename ); 45 | } ); 46 | 47 | it( 'should be unloaded after the change', function() { 48 | assert( !script.loaded ); 49 | } ); 50 | 51 | it( 'should be able to reload the script with the changes implicitly', function( done ) { 52 | script.exports().then( function( script_exports ) { 53 | assert.deepEqual( script_exports, { 54 | test: 42 55 | } ); 56 | assert( script.loaded ); 57 | 58 | } ).then( done ); 59 | } ); 60 | 61 | it( 'should be able to unwatch a file', function() { 62 | script.unwatch(); 63 | 64 | assert( !script.watched ); 65 | } ); 66 | 67 | it( 'should not unload if the file is changed when the script is not watched', function( done ) { 68 | var watcher = fs.watch( script.filename, function() { 69 | assert( script.loaded ); 70 | watcher.close(); 71 | done(); 72 | } ); 73 | 74 | touch( script.filename ); 75 | } ); 76 | } ); 77 | } ); -------------------------------------------------------------------------------- /test/src/5_amd_scripts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/6/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import assert from "assert"; 7 | 8 | describe( `AMD Scripts`, function() { 9 | let Script = Scriptor.Script; 10 | 11 | describe( 'simple script that requires a build-in module', function() { 12 | let script = new Script( './test/fixtures/amd/simple.js', module ); 13 | 14 | it( 'should load without any issues', function( done ) { 15 | script.exports().then( function() { 16 | assert( script.loaded ); 17 | 18 | } ).then( done ); 19 | } ); 20 | } ); 21 | 22 | describe( 'using built-in plugins', function() { 23 | let script = new Script( './test/fixtures/amd/builtin_plugins.js', module ); 24 | 25 | it( 'should load without any issues', function( done ) { 26 | script.exports().then( function() { 27 | assert( script.loaded ); 28 | 29 | } ).then( done ); 30 | } ); 31 | } ); 32 | } ); -------------------------------------------------------------------------------- /test/src/6_text_scripts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/7/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import assert from "assert"; 7 | 8 | describe( `TextScripts`, function() { 9 | let TextScript = Scriptor.TextScript; 10 | 11 | describe( 'simple file with hello world', function() { 12 | let script = new TextScript( './test/fixtures/text/hello.txt', module ); 13 | 14 | it( 'should not export anything', function( done ) { 15 | script.exports().then( function( script_exports ) { 16 | assert.deepEqual( script_exports, {} ); 17 | assert( script.loaded ); 18 | 19 | } ).then( done ); 20 | } ); 21 | 22 | it( 'should give the text as the script source', function( done ) { 23 | script.source( 'utf-8' ).then( src => { 24 | assert.strictEqual( src.trim(), 'Hello, World!' ); 25 | 26 | } ).then( done ); 27 | } ); 28 | } ); 29 | } ); -------------------------------------------------------------------------------- /test/src/7_amd_errors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/7/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import assert from "assert"; 7 | 8 | describe( `Script errors`, function() { 9 | let Script = Scriptor.Script; 10 | 11 | describe( 'simple script that throws an error the first time define is invoked', function() { 12 | let script = new Script( './test/fixtures/amd/error.js', module ); 13 | 14 | it( 'should throw an error the first time', function( done ) { 15 | let hadError = false; 16 | 17 | script.exports().catch( function( err ) { 18 | assert( err instanceof Error ); 19 | assert.deepEqual( script._script.exports, {} ); 20 | assert( script.loaded ); 21 | assert( script.pending ); 22 | 23 | hadError = true; 24 | 25 | } ).then( function() { 26 | assert( hadError, 'No error was thrown' ); 27 | 28 | } ).then( done, done ); 29 | } ); 30 | 31 | it( 'should succeed and finish loading the second time', function( done ) { 32 | script.exports().then( function( script_exports ) { 33 | assert.deepEqual( script_exports, { 34 | test: 42 35 | } ); 36 | assert( script.loaded ); 37 | assert( !script.pending ); 38 | 39 | } ).then( done ); 40 | } ) 41 | } ); 42 | } ); -------------------------------------------------------------------------------- /test/src/8_manager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Aaron on 7/7/2015. 3 | */ 4 | 5 | import Scriptor from "../../"; 6 | import Module from "module"; 7 | import {resolve} from "path"; 8 | import assert from "assert"; 9 | 10 | describe( `Managers`, function() { 11 | let {Manager, Script} = Scriptor; 12 | 13 | let manager; 14 | 15 | describe( 'Creating a manager', function() { 16 | manager = new Manager( module ); 17 | 18 | it( 'should result in an empty manager with a parent module', function() { 19 | assert( manager.parent instanceof Module ); 20 | assert.strictEqual( manager.parent.parent, module ); 21 | assert( manager.scripts instanceof Map ); 22 | assert.strictEqual( manager.scripts.size, 0 ); 23 | } ); 24 | 25 | it( 'should set cwd to process.cwd', function() { 26 | assert.strictEqual( manager.cwd(), process.cwd() ); 27 | } ); 28 | 29 | it( 'should allow changing cwd', function() { 30 | manager.chdir( './fixtures' ); 31 | 32 | assert.strictEqual( manager.cwd(), resolve( process.cwd(), './fixtures' ) ); 33 | } ); 34 | } ); 35 | 36 | describe( 'Add simple scripts through a manager instance', function() { 37 | it( 'should return the Script instance when passed a path relative to the cwd', function() { 38 | let script = manager.add( 'empty.js' ); 39 | 40 | assert( script instanceof Script ); 41 | assert.strictEqual( script.filename, resolve( process.cwd(), './fixtures/empty.js' ) ); 42 | } ); 43 | } ); 44 | } ); --------------------------------------------------------------------------------