diff --git a/CHANGELOG.md b/CHANGELOG.md index 2132785..4f91d9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # changelog +## 0.5.0 + +* Removed `sort` option in favour of ordered minimatch pattern handling via sandermatch +* Handle symlinks in sourcemaps +* Correctly handle newlines in identity sourcemaps +* Ability to handle inline sourcemaps (e.g. from gobble-postcss) +* Use `/*# */` sourcemap comments in CSS files (instead of `//#`) + ## 0.4.0 * Handle sourcemaps diff --git a/README.md b/README.md index f904c50..20d1600 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ var gobble = require( 'gobble' ); module.exports = gobble( 'js' ).transform( 'concat', { dest: 'bundle.js' }); ``` -The `dest` property is required. Other values - `files`, `sort`, `separator` and `writeSourcemap`, explained below - are optional. +The `dest` option is required. Other options - `files`, `separator` and `writeSourcemap`, explained below - are optional. ### `files` @@ -45,11 +45,7 @@ module.exports = gobble( 'js' ) }); ``` -### `sort` - -Within each pattern, if multiple files are found, they will be sorted alphabetically by default. You can override this by passing a `sort` option, which is a standard [compare function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)]. - -If files match multiple patterns, they will only be included once (upon the first match). +If a file matches more than one minimatch pattern, only its first appearance is taken into account. The order of the patterns will decide the order of the files (in the previous example, all the `vendor/` files will appear before any `src` files). ### `separator` diff --git a/get-source-node.js b/get-source-node.js new file mode 100644 index 0000000..a535b52 --- /dev/null +++ b/get-source-node.js @@ -0,0 +1,73 @@ + +// Given a file name, reads it, checks it if has a sourcemap attached, then either: +// - Creates an identity sourcemap if it doesn't exist, +// - Reads the file with the sourcemap, or +// - Decodes an embedded base64 sourcemap + + +var SourceNode = require('source-map').SourceNode; +var SourceMapConsumer = require('source-map').SourceMapConsumer; +var sander = require('sander'), + readFile = sander.readFile, + readFileSync = sander.readFileSync, + realpathSync = sander.realpathSync; + +var sourceMapRegExp = new RegExp(/(?:\/\/#|\/\/@|\/\*#)\s*sourceMappingURL=(\S*)\s*(?:\*\/\s*)?$/); +var dataUriRegexp = new RegExp(/^(data:)([\w\/\+]+)(;charset[=:][\w-]+)?(;base64)?,(.*)/); // From https://github.com/ragingwind/data-uri-regex, modified + + +function getSourceNode(filepath, filename) { + return readFile(filepath, filename).then( srcBuffer => { + + var srcStr = srcBuffer.toString(); + + /// Run a regexp against the code to check for source mappings. + var match = sourceMapRegExp.exec(srcStr); + + if (!match) { + // Create identity sourcemap + var realPath = realpathSync(filepath); + var realFilename = realpathSync(realPath, filename); + var lines = srcStr.split('\n'); + var lineCount = lines.length; + + var identNode = new SourceNode(null, null, null, ''); + identNode.setSourceContent(realFilename, srcStr); + + for (var i=0; i { +// console.log('Loaded external sourcemap for ', filename + ": (" + sourcemapFilename + ")"); + var parsedMap = new SourceMapConsumer( mapContents.toString() ); + return SourceNode.fromStringWithSourceMap( srcStr, parsedMap ); + }, err => { + throw new Error('File ' + filename + ' refers to a non-existing sourcemap at ' + sourcemapFilename + ' ' + err); + }); + } + } + }); +} + +module.exports = getSourceNode; diff --git a/gobble-concat.js b/gobble-concat.js new file mode 100644 index 0000000..97cb822 --- /dev/null +++ b/gobble-concat.js @@ -0,0 +1,75 @@ +// 🦃namespace concat +// Concatenates some files into one file. + +var sander = require( 'sander' ), + sandermatch = require( 'sandermatch' ), + path = require( 'path' ), + mapSeries = require( 'promise-map-series' ), + getSourceNode = require( './get-source-node' ); +var SourceNode = require('source-map').SourceNode; + +var extensionsRegExp = new RegExp(/(\.js|\.css)$/); + + +module.exports = function concat ( inputdir, outputdir, options ) { + + // 🦃option dest: String; The destination filename where everything will be concatenated to. Required. + if ( !options.dest ) { + throw new Error( 'You must pass a \'dest\' option to gobble-concat' ); + } + + // 🦃option writeSourcemap: Boolean; By default, sourcemaps are handled if the destination filename ends in `*.js` or `*.css`. Set this option to override that. + if ( options.writeSourcemap === undefined ) { + options.writeSourcemap = !!options.dest.match( extensionsRegExp ); + } + + // 🦃option files: Minimatch; A Minimatch expression (or an array of Minimatch expressions) matching the files to be concatenated. + return sandermatch.lsrMatch( inputdir, options.files ).then( function ( filenames ) { + var nodes = []; + function getSourceNodes ( filenames ) { + var nodes = []; + + var nodePromises = []; + filenames.forEach(function (filename) { + nodePromises.push(getSourceNode( inputdir, filename )); + }); + return Promise.all(nodePromises); + } + + function writeResult (nodes) { + if (!nodes[0]) { + // Degenerate case: no matched files + return sander.writeFile( outputdir, options.dest, '' ); + } + + // 🦃option separator: String='\n\n'; The string which will be added between files. + var separatorNode = new SourceNode(null, null, null, options.separator || '\n\n'); + + var dest = new SourceNode(null, null, null, ''); + dest.add(nodes[0]); + + for (var i=1; i" @@ -9,15 +9,16 @@ "license": "MIT", "repository": "https://github.com/gobblejs/gobble-concat", "files": [ - "index.js" + "gobble-concat.js" ], + "main": "gobble-concat.js", "keywords": [ "gobble-plugin" ], "dependencies": { - "minimatch": "~1.0.0", "promise-map-series": "~0.2.0", "sander": "^0.1.6", + "sandermatch": "^0.1.4", "source-map": "^0.5.3" } }