├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── expected └── index.js ├── fixtures └── index.js └── main.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [test/*] 16 | insert_final_newline = false 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | temp/ 4 | coverage 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "camelcase": true, 6 | "curly": true, 7 | "eqeqeq": true, 8 | "immed": true, 9 | "indent": 4, 10 | "latedef": true, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "double", 14 | "regexp": true, 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "white": true 21 | } 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Ingvar Stepanyan 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-wrap-js 2 | [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][depstat-image]][depstat-url] 3 | 4 | When using classic templating solutions (like Lo-Dash / Underscore templates / own regexps / etc.) for wrapping JavaScript code, you're manipulating code as strings under the hood, and so losing any inner structure and location information. Such approach is used in [gulp-wrap](https://github.com/adamayres/gulp-wrap), [gulp-wrap-amd](https://github.com/phated/gulp-wrap-amd), [gulp-wrap-umd](https://github.com/phated/gulp-wrap-umd) and that's why source maps are not currently supported in any of them. 5 | 6 | In opposite, this plugin is based on AST templating [estemplate](https://github.com/RReverser/estemplate) library, and allows to wrap your JavaScript code into given template (UMD / AMD / whatever) with preserved locations for source maps generation. 7 | 8 | Check out [gulp-sourcemaps](https://github.com/floridoo/gulp-sourcemaps) for detailed instructions on working with source maps enabled plugins in Gulp. 9 | 10 | ## Usage 11 | 12 | First, install `gulp-wrap-js` as a development dependency: 13 | 14 | ```shell 15 | npm install --save-dev gulp-wrap-js 16 | ``` 17 | 18 | Then, add it to your `gulpfile.js`: 19 | 20 | ```javascript 21 | var sourcemaps = require('gulp-sourcemaps'); 22 | var wrapJS = require("gulp-wrap-js"); 23 | 24 | gulp.src("./src/*.js") 25 | .pipe(sourcemaps.init()) 26 | .pipe(wrapJS('define(function () {%= body %})')) 27 | .pipe(sourcemaps.write()) 28 | .pipe(gulp.dest("./dist")); 29 | ``` 30 | 31 | ## API 32 | 33 | ### wrapJS(template, format) 34 | 35 | #### template 36 | Type: `String` 37 | **Required** 38 | 39 | Template you wish to wrap your code with. Check out [estemplate docs](https://github.com/RReverser/estemplate#estemplatetmplstring-options-data) for all the possible substitution markers. 40 | 41 | Note that in `gulp-wrap-js` you have only `body` array of statements passed to template as data. 42 | 43 | #### format 44 | Type: `Object` 45 | Default: `escodegen.FORMAT_DEFAULTS` 46 | 47 | [escodegen](https://github.com/Constellation/escodegen/wiki/API) output `format` options. 48 | 49 | ## License 50 | 51 | [MIT License](http://en.wikipedia.org/wiki/MIT_License) 52 | 53 | [npm-url]: https://npmjs.org/package/gulp-wrap-js 54 | [npm-image]: https://badge.fury.io/js/gulp-wrap-js.png 55 | 56 | [travis-url]: http://travis-ci.org/RReverser/gulp-wrap-js 57 | [travis-image]: https://secure.travis-ci.org/RReverser/gulp-wrap-js.png?branch=master 58 | 59 | [depstat-url]: https://david-dm.org/RReverser/gulp-wrap-js 60 | [depstat-image]: https://david-dm.org/RReverser/gulp-wrap-js.png 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var through = require('through2'), 2 | gutil = require('gulp-util'), 3 | esprima = require('esprima'), 4 | estemplate = require('estemplate'), 5 | escodegen = require('escodegen'), 6 | applySourceMap = require('vinyl-sourcemaps-apply'), 7 | path = require('path'); 8 | 9 | module.exports = function (tmpl, format) { 10 | 'use strict'; 11 | 12 | if (!tmpl) { 13 | throw new gutil.PluginError('gulp-wrap-js', 'No template supplied'); 14 | } 15 | 16 | tmpl = estemplate.compile(tmpl, { attachComment:true }); 17 | format = format || escodegen.FORMAT_DEFAULTS; 18 | 19 | return through.obj(function (file, enc, callback) { 20 | /*jshint validthis:true*/ 21 | 22 | // Do nothing if no contents 23 | if (file.isNull()) { 24 | return callback(null, file); 25 | } 26 | 27 | if (file.isStream()) { 28 | return callback(new gutil.PluginError('gulp-wrap-js', 'Stream content is not supported')); 29 | } 30 | 31 | // check if file.contents is a `Buffer` 32 | if (file.isBuffer()) { 33 | try { 34 | var ast = esprima.parse(file.contents, { 35 | loc: true, 36 | source: file.relative, 37 | range: true, 38 | tokens: true, 39 | comment: true 40 | }); 41 | escodegen.attachComments(ast, ast.comments, ast.tokens); 42 | ast.file = file; 43 | ast = tmpl(ast); 44 | var result = escodegen.generate(ast, { 45 | comment: true, 46 | format: format, 47 | sourceMap: true, 48 | sourceMapWithCode: true, 49 | file: file.relative 50 | }); 51 | } catch(e) { 52 | // Relative to gulpfile.js filepath with forward slashes 53 | file = gutil.colors.magenta(path.relative('.', file.path).split(path.sep).join('/')); 54 | return callback(new gutil.PluginError('gulp-wrap-js', file + ' ' + e.message)) 55 | } 56 | 57 | file.contents = new Buffer(result.code); 58 | if (file.sourceMap) { 59 | applySourceMap(file, JSON.parse(result.map.toString())); 60 | } 61 | return callback(null, file); 62 | } 63 | }); 64 | }; 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-wrap-js", 3 | "version": "0.4.1", 4 | "description": "Gulp plugin for templating JavaScript code with source maps support.", 5 | "keywords": [ 6 | "gulpplugin", 7 | "js", 8 | "ast", 9 | "code", 10 | "wrap", 11 | "template", 12 | "sourcemaps" 13 | ], 14 | "author": { 15 | "name": "Ingvar Stepanyan", 16 | "email": "me@rreverser.com", 17 | "url": "https://github.com/RReverser" 18 | }, 19 | "repository": "RReverser/gulp-wrap-js", 20 | "scripts": { 21 | "test": "mocha --reporter spec" 22 | }, 23 | "dependencies": { 24 | "escodegen": "^1.6.1", 25 | "esprima": "^2.3.0", 26 | "estemplate": "*", 27 | "gulp-util": "~3.0.5", 28 | "through2": "*", 29 | "vinyl-sourcemaps-apply": "^0.1.4" 30 | }, 31 | "devDependencies": { 32 | "gulp": "^3.9.0", 33 | "gulp-sourcemaps": "^1.5.2", 34 | "mocha": "*", 35 | "should": "^6.0.3", 36 | "stream-assert": "^2.0.2" 37 | }, 38 | "engines": { 39 | "node": ">=0.8.0", 40 | "npm": ">=1.2.10" 41 | }, 42 | "license": "MIT" 43 | } 44 | -------------------------------------------------------------------------------- /test/expected/index.js: -------------------------------------------------------------------------------- 1 | // template comment 2 | define('index.js', function () { 3 | var x = 1; 4 | var y = 2; 5 | // Comment 6 | console.log(x + y); 7 | }); 8 | -------------------------------------------------------------------------------- /test/fixtures/index.js: -------------------------------------------------------------------------------- 1 | var x = 1; 2 | var y = 2; 3 | // Comment 4 | console.log(x + y); 5 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | /*global describe, it*/ 2 | 'use strict'; 3 | 4 | var fs = require('fs'), 5 | gulp = require('gulp'), 6 | should = require('should'), 7 | assert = require('stream-assert'), 8 | sourcemaps = require('gulp-sourcemaps'); 9 | 10 | require('mocha'); 11 | 12 | delete require.cache[require.resolve('../')]; 13 | 14 | var gutil = require('gulp-util'), 15 | wrapJS = require('../'); 16 | 17 | describe('gulp-wrap-js', function () { 18 | var expectedFile = new gutil.File({ 19 | path: 'test/expected/index.js', 20 | cwd: 'test/', 21 | base: 'test/expected', 22 | contents: fs.readFileSync('test/expected/index.js') 23 | }); 24 | 25 | it('should produce expected file and source map', function (done) { 26 | gulp.src('test/fixtures/index.js') 27 | .pipe(sourcemaps.init()) 28 | .pipe(wrapJS('// template comment\ndefine("%= file.relative %", function () {%= body %});')) 29 | .pipe(assert.first(function (file) { 30 | file.contents.toString().should.eql(expectedFile.contents.toString().trim()); 31 | file.sourceMap.sources.should.eql([file.relative]); 32 | file.sourceMap.file.should.eql(expectedFile.relative); 33 | file.sourceMap.names.should.not.be.empty; 34 | })) 35 | .pipe(assert.end(done)); 36 | }); 37 | }); 38 | --------------------------------------------------------------------------------