Initial commit (a clone of the jsmachines project as of v1.15.3)
This commit is contained in:
commit
a5e3e6a59d
714 changed files with 130602 additions and 0 deletions
13
my_modules/grunts/prepjs/.jshintrc
Normal file
13
my_modules/grunts/prepjs/.jshintrc
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"curly": true,
|
||||
"eqeqeq": true,
|
||||
"immed": true,
|
||||
"latedef": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"sub": true,
|
||||
"undef": true,
|
||||
"boss": true,
|
||||
"eqnull": true,
|
||||
"node": true
|
||||
}
|
||||
143
my_modules/grunts/prepjs/Gruntfile.js
Normal file
143
my_modules/grunts/prepjs/Gruntfile.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* grunt-prepjs
|
||||
* https://github.com/jeffpar/jsmachines
|
||||
*
|
||||
* Copyright (c) 2014 jeffpar
|
||||
* Licensed under the MIT license.
|
||||
*
|
||||
* Genesis:
|
||||
*
|
||||
* sudo npm install -g grunt-init
|
||||
* git clone git://github.com/gruntjs/grunt-init-gruntplugin.git ~/.grunt-init/gruntplugin
|
||||
* cd ~/Sites/pcjs/my_modules
|
||||
* mkdir -p grunts/prepjs
|
||||
* cd grunts/prepjs
|
||||
* grunt-init gruntplugin
|
||||
* npm install
|
||||
*
|
||||
* This file was generated at the "grunt-init gruntplugin" stage. Here's what that process looked like:
|
||||
*
|
||||
* Running "init:gruntplugin" (init) task
|
||||
* This task will create one or more files in the current directory, based on the
|
||||
* environment and the answers to a few questions. Note that answering "?" to any
|
||||
* question will show question-specific help and answering "none" to most questions
|
||||
* will leave its value blank.
|
||||
*
|
||||
* "gruntplugin" template notes:
|
||||
* For more information about Grunt plugin best practices, please see the docs at
|
||||
* http://gruntjs.com/creating-plugins
|
||||
*
|
||||
* Please answer the following:
|
||||
* [?] Project name (grunt-prep) grunt-prepjs
|
||||
* [?] Description (The best Grunt plugin ever.) JS Preprocessor
|
||||
* [?] Version (0.1.0)
|
||||
* [?] Project git repository (git://github.com/jeffpar/jsmachines.git)
|
||||
* [?] Project homepage (https://github.com/jeffpar/jsmachines)
|
||||
* [?] Project issues tracker (https://github.com/jeffpar/jsmachines/issues)
|
||||
* [?] Licenses (MIT)
|
||||
* [?] Author name (jeffpar)
|
||||
* [?] Author email (jeffpar@mac.com)
|
||||
* [?] Author url (none)
|
||||
* [?] What versions of grunt does it require? (~0.4.4)
|
||||
* [?] What versions of node does it run on? (>= 0.8.0)
|
||||
* [?] Do you need to make any changes to the above before continuing? (y/N)
|
||||
*
|
||||
* Writing .gitignore...OK
|
||||
* Writing .jshintrc...OK
|
||||
* Writing Gruntfile.js...OK
|
||||
* Writing README.md...OK
|
||||
* Writing tasks/prepjs.js...OK
|
||||
* Writing test/expected/custom_options...OK
|
||||
* Writing test/expected/default_options...OK
|
||||
* Writing test/fixtures/123...OK
|
||||
* Writing test/fixtures/testing...OK
|
||||
* Writing test/prepjs_test.js...OK
|
||||
* Writing LICENSE-MIT...OK
|
||||
* Writing package.json...OK
|
||||
*
|
||||
* Initialized from template "gruntplugin".
|
||||
* You should now install project dependencies with npm install. After that, you
|
||||
* may execute project tasks with grunt. For more information about installing
|
||||
* and configuring Grunt, please see the Getting Started guide:
|
||||
*
|
||||
* http://gruntjs.com/getting-started
|
||||
*
|
||||
* Done, without errors.
|
||||
*
|
||||
* The process was a bit sloppy about trailing commas, though. I've cleaned those up, thanks to PhpStorm.
|
||||
*
|
||||
* Online tutorials further recommended the following in my project's root:
|
||||
*
|
||||
* npm install my_modules/grunts/prepjs --save-dev
|
||||
*
|
||||
* However, all that does is make another copy of my "grunt-prepjs" module, inside the "node_modules" folders;
|
||||
* I can avoid that by simply including the following in my root Gruntfile.js:
|
||||
*
|
||||
* grunt.loadTasks("my_modules/grunts/prepjs/tasks");
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
jshint: {
|
||||
all: [
|
||||
'Gruntfile.js',
|
||||
'tasks/*.js',
|
||||
'<%= nodeunit.tests %>'
|
||||
],
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
}
|
||||
},
|
||||
|
||||
// Before generating any new files, remove any previously-created files.
|
||||
clean: {
|
||||
tests: ['tmp']
|
||||
},
|
||||
|
||||
// Configuration to be run (and then tested).
|
||||
prepjs: {
|
||||
default_options: {
|
||||
options: {
|
||||
},
|
||||
files: {
|
||||
'tmp/default_options': ['test/fixtures/testing', 'test/fixtures/123']
|
||||
}
|
||||
},
|
||||
custom_options: {
|
||||
options: {
|
||||
separator: ': ',
|
||||
punctuation: ' !!!'
|
||||
},
|
||||
files: {
|
||||
'tmp/custom_options': ['test/fixtures/testing', 'test/fixtures/123']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Unit tests.
|
||||
nodeunit: {
|
||||
tests: ['test/*_test.js']
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Actually load this plugin's task(s).
|
||||
grunt.loadTasks('tasks');
|
||||
|
||||
// These plugins provide necessary tasks.
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-nodeunit');
|
||||
|
||||
// Whenever the "test" task is run, first clean the "tmp" dir, then run this
|
||||
// plugin's task(s), then test the result.
|
||||
grunt.registerTask('test', ['clean', 'prepjs', 'nodeunit']);
|
||||
|
||||
// By default, lint and run all tests.
|
||||
grunt.registerTask('default', ['jshint', 'test']);
|
||||
|
||||
};
|
||||
22
my_modules/grunts/prepjs/LICENSE-MIT
Normal file
22
my_modules/grunts/prepjs/LICENSE-MIT
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2014 jeffpar
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
89
my_modules/grunts/prepjs/README.md
Normal file
89
my_modules/grunts/prepjs/README.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# grunt-prepjs
|
||||
|
||||
> JS Preprocessor
|
||||
|
||||
## Getting Started
|
||||
This plugin requires Grunt `~0.4.4`
|
||||
|
||||
If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:
|
||||
|
||||
```shell
|
||||
npm install grunt-prepjs --save-dev
|
||||
```
|
||||
|
||||
Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:
|
||||
|
||||
```js
|
||||
grunt.loadNpmTasks('grunt-prepjs');
|
||||
```
|
||||
|
||||
## The "prepjs" task
|
||||
|
||||
### Overview
|
||||
In your project's Gruntfile, add a section named `prepjs` to the data object passed into `grunt.initConfig()`.
|
||||
|
||||
```js
|
||||
grunt.initConfig({
|
||||
prepjs: {
|
||||
options: {
|
||||
// Task-specific options go here.
|
||||
},
|
||||
your_target: {
|
||||
// Target-specific file lists and/or options go here.
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
#### options.separator
|
||||
Type: `String`
|
||||
Default value: `', '`
|
||||
|
||||
A string value that is used to do something with whatever.
|
||||
|
||||
#### options.punctuation
|
||||
Type: `String`
|
||||
Default value: `'.'`
|
||||
|
||||
A string value that is used to do something else with whatever else.
|
||||
|
||||
### Usage Examples
|
||||
|
||||
#### Default Options
|
||||
In this example, the default options are used to do something with whatever. So if the `testing` file has the content `Testing` and the `123` file had the content `1 2 3`, the generated result would be `Testing, 1 2 3.`
|
||||
|
||||
```js
|
||||
grunt.initConfig({
|
||||
prepjs: {
|
||||
options: {},
|
||||
files: {
|
||||
'dest/default_options': ['src/testing', 'src/123'],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Custom Options
|
||||
In this example, custom options are used to do something else with whatever else. So if the `testing` file has the content `Testing` and the `123` file had the content `1 2 3`, the generated result in this case would be `Testing: 1 2 3 !!!`
|
||||
|
||||
```js
|
||||
grunt.initConfig({
|
||||
prepjs: {
|
||||
options: {
|
||||
separator: ': ',
|
||||
punctuation: ' !!!',
|
||||
},
|
||||
files: {
|
||||
'dest/default_options': ['src/testing', 'src/123'],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Contributing
|
||||
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).
|
||||
|
||||
## Release History
|
||||
_(Nothing yet)_
|
||||
41
my_modules/grunts/prepjs/package.json
Normal file
41
my_modules/grunts/prepjs/package.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "grunt-prepjs",
|
||||
"description": "JS Preprocessor",
|
||||
"version": "0.1.0",
|
||||
"homepage": "https://github.com/jeffpar/jsmachines",
|
||||
"author": {
|
||||
"name": "jeffpar",
|
||||
"email": "jeffpar@mac.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/jeffpar/jsmachines.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/jeffpar/jsmachines/issues"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/jeffpar/jsmachines/blob/master/LICENSE-MIT"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt-contrib-jshint": "~0.6.0",
|
||||
"grunt-contrib-clean": "~0.4.0",
|
||||
"grunt-contrib-nodeunit": "~0.2.0",
|
||||
"grunt": "~0.4.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"grunt": "~0.4.4"
|
||||
},
|
||||
"keywords": [
|
||||
"gruntplugin"
|
||||
]
|
||||
}
|
||||
401
my_modules/grunts/prepjs/tasks/prepjs.js
Normal file
401
my_modules/grunts/prepjs/tasks/prepjs.js
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
/**
|
||||
* @fileoverview Pre-process JavaScript file(s) with well-defined constants inlined
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a> (@jeffpar)
|
||||
* @version 1.1
|
||||
* Created 2014-Mar-22
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
*
|
||||
* This file is part of C1Pjs, PCjs, and other related components written
|
||||
* by Jeff Parsons and originally published at cpusim.org and jsmachines.net.
|
||||
*
|
||||
* C1Pjs and PCjs are free software: you can redistribute them and/or modify
|
||||
* them under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* C1Pjs and PCjs are distributed in the hope that they will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with C1Pjs and PCjs. If not, see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source
|
||||
* code file of every copy or modified version of this work, and to display
|
||||
* that copyright notice on every screen that loads or runs any version
|
||||
* of this software (see Computer.sCopyright).
|
||||
*
|
||||
* Some C1Pjs and PCjs files also attempt to load external resource files, such
|
||||
* as character-image files and ROM files. Those external resource files are
|
||||
* not considered part of the PCjs program for purposes of the GNU General Public
|
||||
* License, and the author does not claim any copyright as to their contents.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Options
|
||||
* ---
|
||||
* The 'includeObjectConstants' option allows replacement of constants defined
|
||||
* within objects, instead of only global constants. Use with caution, because
|
||||
* constants defined within an object scope may not be unique. This does attempt
|
||||
* to catch any constant collisions and completely disable their replacement, but
|
||||
* it's not foolproof.
|
||||
*
|
||||
* History
|
||||
* ---
|
||||
* Although we run all our code through Google's Closure Compiler, which does a
|
||||
* great job of inlining not only variables but also code to improve performance,
|
||||
* JavaScript doesn't have the notion of "constants", so we have to define all our
|
||||
* constants as properties and simply trust that the Closure Compiler will inline
|
||||
* them all. I'm not completely trusting, so I've created this script that allows
|
||||
* us to verify the compiler produces substantially the same code whether or not
|
||||
* we inline all our constants first.
|
||||
*
|
||||
* See "inline.php" for the original PHP version of this module.
|
||||
*
|
||||
* Conventions
|
||||
* ---
|
||||
* This script looks for global constant definitions of the form "Component.XXX = YYY;".
|
||||
*
|
||||
* The current approach requires all inlined constants to be defined as properties on
|
||||
* the associated class constructor (ie, as "class constants"). Life might be simpler
|
||||
* if the Closure Compiler honored "@const" references for properties, and who knows,
|
||||
* perhaps the latest version does now; but on the other hand, having my own convention
|
||||
* relieves me from having to annotate every single constant with a "@const" JSDoc tag.
|
||||
*
|
||||
* TODO: Update the C1Pjs sources to use class constants instead of "object constants",
|
||||
* because in order for C1Pjs to benefit from constant inlining, we must reply on the
|
||||
* 'includeObjectConstants' option (hack) to expand the contexts that constants may live
|
||||
* in, which is inherently less safe. Moreover, that option prevents us from removing
|
||||
* the original constant definitions, because constants like "this.PORT_CRA" could be used
|
||||
* in other contexts that this script will NOT catch (eg, "controller.PORT_CRA").
|
||||
*
|
||||
* TODO: Think about adding another quick hack to this tool, to convert all:
|
||||
*
|
||||
* at-param {Debugger} dbg
|
||||
* to:
|
||||
* at-param {Component} dbg
|
||||
*
|
||||
* prior to compilation. The only reason I declared my "dbg" variables generically,
|
||||
* as Component objects rather than Debugger objects, was to work around compilation
|
||||
* errors in the non-Debugger builds.
|
||||
*
|
||||
* Implementation
|
||||
* ---
|
||||
* This script uses a very simplistic replacement approach that doesn't perform any
|
||||
* parsing, tokenizing or other pre-processing of the source code, which would otherwise
|
||||
* be required if we wanted to guarantee that all our replacements precisely mirrored what
|
||||
* JavaScript actually replaces at run-time. For example, JavaScript allows any so-called
|
||||
* constant to be redefined at any point, and we don't attempt to catch modifications.
|
||||
*
|
||||
* This is why it's important that we limit inlining to only those constants described
|
||||
* above, and why such constants must never be altered by the code using them.
|
||||
*
|
||||
* Debugging
|
||||
* ===
|
||||
* Use the following command to debug this task (after making Chrome your default browser):
|
||||
*
|
||||
* node-debug $(which grunt) prepjs
|
||||
*
|
||||
* which required installing "node-inspector" first:
|
||||
*
|
||||
* sudo npm install -g node-inspector
|
||||
*
|
||||
* You may also want to enable the heapdump code below, which required installing "heapdump" first:
|
||||
*
|
||||
* npm install heapdump --save-dev
|
||||
*
|
||||
* TODO: Resolve once and for all the "process out of memory" error that occurs if we don't divide
|
||||
* the src input into smaller chunks. See the WARNING below.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// var heapdump = require("heapdump");
|
||||
|
||||
/**
|
||||
* compareConstants(a, b)
|
||||
*
|
||||
* @param {Array} a
|
||||
* @param {Array} b
|
||||
* @returns {number}
|
||||
*/
|
||||
var compareConstants = function(a, b)
|
||||
{
|
||||
return b[0].length - a[0].length;
|
||||
};
|
||||
|
||||
/**
|
||||
* indexOfConstant(sConstant, aConstants)
|
||||
*
|
||||
* @param {string} sConstant
|
||||
* @param {Array} aConstants
|
||||
* @returns {number} index of aConstants entry, or -1 if not found
|
||||
*/
|
||||
var indexOfConstant = function(sConstant, aConstants)
|
||||
{
|
||||
for (var i = 0; i < aConstants.length; i++) {
|
||||
if (sConstant == aConstants[i][0]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* findConstants(sInput)
|
||||
*
|
||||
* @param {string} sInput
|
||||
* @param {Array} aConstants
|
||||
* @param {boolean} fObjectConstants
|
||||
* @param {boolean} fReplaceConstants
|
||||
* @param {function} [fnLog]
|
||||
* @returns {string} modified input
|
||||
*/
|
||||
var findConstants = function(sInput, aConstants, fObjectConstants, fReplaceConstants, fnLog) {
|
||||
var sWarning = "";
|
||||
var sConstDef = "[A-Z][A-Za-z0-9_]*";
|
||||
if (fObjectConstants) {
|
||||
sConstDef = "(?:" + sConstDef + "|this)";
|
||||
}
|
||||
var aConstDef;
|
||||
var reConstDef = new RegExp("[ \t]*(" + sConstDef + "\\.[A-Z_][A-Z0-9_\\.]*)\\s*=\\s*(.*?)\\s*;[\t ]*(?://[^\n]*|)\n", "g");
|
||||
while (aConstDef = reConstDef.exec(sInput)) {
|
||||
var i;
|
||||
var sFind = aConstDef[1];
|
||||
var sReplace = aConstDef[2];
|
||||
if (fReplaceConstants && !fObjectConstants) {
|
||||
sInput = sInput.substr(0, aConstDef.index) + sInput.substr(aConstDef.index + aConstDef[0].length);
|
||||
reConstDef.lastIndex -= aConstDef[0].length;
|
||||
}
|
||||
if ((i = indexOfConstant(sFind, aConstants)) >= 0) {
|
||||
sWarning += "/*\n * warning: multiple definitions for '" + sFind + "' (" + sReplace + ")\n */\n";
|
||||
aConstants[i][2] = -1; // set a negative replacement count to disable this constant definition
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* If the replacement string is entirely quoted, or parenthesized, or a single constant, then we can leave
|
||||
* the replacement string as-is; otherwise, let's wrap it with parentheses (we could probably wrap everything
|
||||
* with parentheses, but I like to avoid doing that whenever it's completely unnecessary).
|
||||
*/
|
||||
if (!sReplace.match(/^["'\(].*["'\)]$/) && sReplace.match(/[^A-Za-z0-9\.]/)) {
|
||||
sReplace = "(" + sReplace + ")";
|
||||
}
|
||||
if (fnLog) {
|
||||
fnLog("found '" + sFind + "' => '" + sReplace + "'");
|
||||
/*
|
||||
if (sFind == "Video.CRT.CURSOR_END") {
|
||||
fnLog("here's where we usually run of memory (" + process.cwd() + ")");
|
||||
heapdump.writeSnapshot();
|
||||
}
|
||||
*/
|
||||
}
|
||||
aConstants.push([sFind, sReplace, 0]);
|
||||
}
|
||||
if (sWarning) sInput = sWarning + sInput;
|
||||
return sInput;
|
||||
};
|
||||
|
||||
/**
|
||||
* replaceConstants(sInput, aConstants, fObjectConstants, fInConstant, fnLog)
|
||||
*
|
||||
* @param {string} sInput
|
||||
* @param {Array} aConstants
|
||||
* @param {boolean} fObjectConstants
|
||||
* @param {boolean} [fInConstant]
|
||||
* @param {function} [fnLog]
|
||||
* @returns {string} modified input
|
||||
*/
|
||||
var replaceConstants = function(sInput, aConstants, fObjectConstants, fInConstant, fnLog)
|
||||
{
|
||||
do {
|
||||
var cReplacements = 0;
|
||||
for (var i = 0; i < aConstants.length; i++) {
|
||||
if (aConstants[i][2] < 0) continue; // skip any replacement for which we recorded multiple definitions (ie, negative replacement count)
|
||||
var sFind = aConstants[i][0];
|
||||
var sReplace = aConstants[i][1];
|
||||
var cchFind = sFind.length;
|
||||
var cchReplace = sReplace.length;
|
||||
var iNext = 0;
|
||||
while ((iNext = sInput.indexOf(sFind, iNext)) >= 0) {
|
||||
if (fObjectConstants) {
|
||||
/*
|
||||
* As discussed earlier, the 'includeObjectConstants' option precludes removing any constant definitions,
|
||||
* so we must additionally ensure that we don't inadvertently perform replacements on those definitions.
|
||||
*/
|
||||
if (sInput.substr(iNext, 1024).match(/([A-Za-z][A-Za-z0-9_]*\.[A-Z_][A-Z0-9_\.]*)\s*=\s*(.*?)\s*;[\t ]*(?:\/\/[^\n]*|)\n/)) {
|
||||
iNext += cchFind;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (fnLog) {
|
||||
fnLog("replaced '" + sFind + "' with '" + sReplace + "'");
|
||||
}
|
||||
sInput = sInput.substr(0, iNext) + sReplace + sInput.substr(iNext + cchFind);
|
||||
aConstants[i][2]++;
|
||||
cReplacements++;
|
||||
iNext += cchReplace;
|
||||
}
|
||||
/*
|
||||
* If we've just done any replacements within another constant, then start the process over again
|
||||
* with the longest constant, to ensure we don't perform any partial replacements.
|
||||
*/
|
||||
if (fInConstant && cReplacements) break;
|
||||
}
|
||||
} while (cReplacements);
|
||||
return sInput;
|
||||
};
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// Please see the Grunt documentation for more information regarding task
|
||||
// creation: http://gruntjs.com/creating-tasks
|
||||
|
||||
grunt.registerMultiTask('prepjs', 'JS Preprocessor', function() {
|
||||
/*
|
||||
* Merge task-specific and/or target-specific options with these defaults.
|
||||
*/
|
||||
var options = this.options({
|
||||
includeObjectConstants: false,
|
||||
listConstants: true,
|
||||
replaceConstants: true
|
||||
});
|
||||
/*
|
||||
* Iterate over all specified file groups.
|
||||
*
|
||||
* See http://gruntjs.com/inside-tasks#this.files, which says in part:
|
||||
*
|
||||
* Your task should iterate over the this.files array, utilizing the src and dest
|
||||
* properties of each object in that array. The this.files property will always be an array.
|
||||
* The src property will also always be an array, in case your task cares about multiple
|
||||
* source files per destination file.
|
||||
*
|
||||
* And http://gruntjs.com/configuring-tasks#files-array-format, which explains that all files
|
||||
* objects support src and dest but the 'Files Array' format supports a few additional properties:
|
||||
*
|
||||
* filter: Either a valid fs.Stats method name or a function that is passed the matched
|
||||
* src filepath and returns true or false;
|
||||
*
|
||||
* nonull: If set to true then the operation will include non-matching patterns. Combined
|
||||
* with grunt's --verbose flag, this option can help debug file path issues;
|
||||
*
|
||||
* dot: Allow patterns to match filenames starting with a period, even if the pattern does
|
||||
* not explicitly have a period in that spot;
|
||||
*
|
||||
* matchBase: If set, patterns without slashes will be matched against the basename of the path
|
||||
* if it contains slashes. For example, a?b would match the path /xyz/123/acb, but not /xyz/acb/123;
|
||||
*
|
||||
* expand: Process a dynamic src-dest file mapping, see "Building the files object dynamically"
|
||||
* for more information.
|
||||
*/
|
||||
this.files.forEach(function(file) {
|
||||
|
||||
/*
|
||||
* Allow any of our options to be set at the target level as well
|
||||
*/
|
||||
var fObjectConstants = file.includeObjectConstants;
|
||||
if (fObjectConstants === undefined) fObjectConstants = options.includeObjectConstants;
|
||||
|
||||
var fListConstants = file.listConstants;
|
||||
if (fListConstants === undefined) fListConstants = options.listConstants;
|
||||
|
||||
var fReplaceConstants = file.replaceConstants;
|
||||
if (fReplaceConstants === undefined) fReplaceConstants = options.replaceConstants;
|
||||
|
||||
/*
|
||||
* Read all file contents into src
|
||||
*/
|
||||
var src = file.src.filter(function(sFilePath) {
|
||||
/*
|
||||
* Warn on and remove invalid source files (if nonull was set)
|
||||
*/
|
||||
if (!grunt.file.exists(sFilePath)) {
|
||||
grunt.log.warn('Source file "' + sFilePath + '" not found.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}).map(function(sFilePath) {
|
||||
/*
|
||||
* Read a file
|
||||
*/
|
||||
return grunt.file.read(sFilePath);
|
||||
}).join("\n");
|
||||
|
||||
/*
|
||||
* Find all the constants in src, and remove their definitions from src if possible
|
||||
* (ie, if fReplaceConstants is true and fObjectConstants is false).
|
||||
*
|
||||
* aConstants is the array of constant definitions, where each definition is another
|
||||
* 3-element array:
|
||||
*
|
||||
* [0]: the original string (ie, the name of the constant)
|
||||
* [1]: the replacement string (ie, the value of the constant)
|
||||
* [2]: a replacement count, initialized to zero; set to -1 if a duplicate is found
|
||||
*
|
||||
* WARNING: Grunt (ie, Node) fails with the following error:
|
||||
*
|
||||
* FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
|
||||
*
|
||||
* when processing a large src stream (eg, on the order 1.5Mb), and it always seems to
|
||||
* die in findConstants()'s call to RegExp's exec() method. I couldn't glean any clues
|
||||
* from the heapdump (other than observing that, yes, running Grunt generates a shitload of
|
||||
* objects and is probably not very well-tuned), so I've implemented a simple work-around:
|
||||
* divide the src stream into two parts. If necessary, this work-around can easily be
|
||||
* generalized to N parts. If the problem is a direct side-effect of passing very large
|
||||
* strings to the RegExp library, then this is clearly one way to avoid that problem.
|
||||
*/
|
||||
var aConstants = [];
|
||||
var i = src.length/2;
|
||||
i = src.indexOf("\n", i) + 1; // divide the src after the first linefeed beyond the midpoint
|
||||
var src1 = src.substr(0, i);
|
||||
var src2 = src.substr(i);
|
||||
// grunt.log.writeln("findConstants(src1): " + src1.length + " chars");
|
||||
src1 = findConstants(src1, aConstants, fObjectConstants, fReplaceConstants);
|
||||
// grunt.log.writeln("findConstants(src2): " + src2.length + " chars");
|
||||
src2 = findConstants(src2, aConstants, fObjectConstants, fReplaceConstants);
|
||||
|
||||
/*
|
||||
* Sort the constants in order of longest definition to shortest, because we perform all the replacements
|
||||
* in array order, and we can't allow definitions that are subsets of longer definitions to be replaced first.
|
||||
*/
|
||||
// grunt.log.writeln("aConstants.sort()");
|
||||
aConstants.sort(compareConstants);
|
||||
|
||||
// grunt.log.writeln("replacing constants in other constants");
|
||||
aConstants.forEach(function(constant) {
|
||||
constant[1] = replaceConstants(constant[1], aConstants, fObjectConstants, true);
|
||||
});
|
||||
|
||||
if (fReplaceConstants) {
|
||||
// grunt.log.writeln("replaceConstants(src1)");
|
||||
src1 = replaceConstants(src1, aConstants, fObjectConstants, false);
|
||||
// grunt.log.writeln("replaceConstants(src2)");
|
||||
src2 = replaceConstants(src2, aConstants, fObjectConstants, false);
|
||||
// grunt.log.writeln("replaceConstants() complete");
|
||||
}
|
||||
|
||||
var sListing = "";
|
||||
|
||||
if (fListConstants) {
|
||||
// grunt.log.writeln("listing constants");
|
||||
sListing += "/*\n * List of grunt-prepjs replacements:\n *\n";
|
||||
for (i = 0; i < aConstants.length; i++) {
|
||||
sListing += " * " + aConstants[i][0] + " => " + aConstants[i][1] + " (" + aConstants[i][2] + " occurrences)\n";
|
||||
}
|
||||
sListing += " */\n";
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the destination file
|
||||
*/
|
||||
// grunt.log.writeln("writing " + f.dest);
|
||||
grunt.file.write(file.dest, sListing + src1 + src2);
|
||||
|
||||
/*
|
||||
* Print a success message
|
||||
*/
|
||||
grunt.log.writeln('File "' + file.dest + '" created.');
|
||||
});
|
||||
});
|
||||
};
|
||||
1
my_modules/grunts/prepjs/test/expected/custom_options
Normal file
1
my_modules/grunts/prepjs/test/expected/custom_options
Normal file
|
|
@ -0,0 +1 @@
|
|||
Testing: 1 2 3 !!!
|
||||
1
my_modules/grunts/prepjs/test/expected/default_options
Normal file
1
my_modules/grunts/prepjs/test/expected/default_options
Normal file
|
|
@ -0,0 +1 @@
|
|||
Testing, 1 2 3.
|
||||
1
my_modules/grunts/prepjs/test/fixtures/123
vendored
Normal file
1
my_modules/grunts/prepjs/test/fixtures/123
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
1 2 3
|
||||
1
my_modules/grunts/prepjs/test/fixtures/testing
vendored
Normal file
1
my_modules/grunts/prepjs/test/fixtures/testing
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
Testing
|
||||
48
my_modules/grunts/prepjs/test/prepjs_test.js
Normal file
48
my_modules/grunts/prepjs/test/prepjs_test.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
'use strict';
|
||||
|
||||
var grunt = require('grunt');
|
||||
|
||||
/*
|
||||
======== A Handy Little Nodeunit Reference ========
|
||||
https://github.com/caolan/nodeunit
|
||||
|
||||
Test methods:
|
||||
test.expect(numAssertions)
|
||||
test.done()
|
||||
Test assertions:
|
||||
test.ok(value, [message])
|
||||
test.equal(actual, expected, [message])
|
||||
test.notEqual(actual, expected, [message])
|
||||
test.deepEqual(actual, expected, [message])
|
||||
test.notDeepEqual(actual, expected, [message])
|
||||
test.strictEqual(actual, expected, [message])
|
||||
test.notStrictEqual(actual, expected, [message])
|
||||
test.throws(block, [error], [message])
|
||||
test.doesNotThrow(block, [error], [message])
|
||||
test.ifError(value)
|
||||
*/
|
||||
|
||||
exports.prepjs = {
|
||||
setUp: function(done) {
|
||||
// setup here if necessary
|
||||
done();
|
||||
},
|
||||
default_options: function(test) {
|
||||
test.expect(1);
|
||||
|
||||
var actual = grunt.file.read('tmp/default_options');
|
||||
var expected = grunt.file.read('test/expected/default_options');
|
||||
test.equal(actual, expected, 'should describe what the default behavior is.');
|
||||
|
||||
test.done();
|
||||
},
|
||||
custom_options: function(test) {
|
||||
test.expect(1);
|
||||
|
||||
var actual = grunt.file.read('tmp/custom_options');
|
||||
var expected = grunt.file.read('test/expected/custom_options');
|
||||
test.equal(actual, expected, 'should describe what the custom option(s) behavior is.');
|
||||
|
||||
test.done();
|
||||
},
|
||||
};
|
||||
Loading…
Reference in a new issue