Browse Source

oh man, never run git reset HEAD while you have unmerged paths. chaos

contingency-plan
Rich-Harris 10 years ago
parent
commit
f873e70ff5
  1. 1
      .gitignore
  2. 25
      CHANGELOG.md
  3. 15
      README.md
  4. 7
      bin/runRollup.js
  5. 5
      package.json
  6. 20
      src/Bundle.js
  7. 2
      src/Module.js
  8. 15
      src/Statement.js
  9. 7
      src/finalisers/amd.js
  10. 25
      src/finalisers/es6.js
  11. 29
      src/finalisers/iife.js
  12. 24
      src/finalisers/umd.js
  13. 32
      src/rollup.js
  14. 5
      src/utils/ensureArray.js
  15. 10
      src/utils/load.js
  16. 53
      src/utils/resolvePath.js

1
.gitignore

@ -2,3 +2,4 @@
node_modules node_modules
.gobble* .gobble*
dist dist
_actual

25
CHANGELOG.md

@ -1,5 +1,30 @@
# rollup changelog # rollup changelog
## 0.6.4
* Fix CJS bundling with default export
## 0.6.3
* Fix exports and external module imports with some output formats
* Fix endless cycle bug on Windows ([#3](https://github.com/rollup/rollup/pull/3)) - thanks @Bobris
## 0.6.2
* Permit assignments to properties of imported bindings
## 0.6.1
* Support for basic transformers
## 0.6.0
* BREAKING - `rollup.rollup` and `bundle.write` both take a single options argument
* BREAKING - external modules must be declared upfront with `options.external: [...]`
* Non-relative module paths will be resolved by looking for `jsnext:main` fields in the appropriate `package.json` files. This behaviour can be overridden by passing an alternative `resolveExternal` function
* Fix sourcemap options
* Include CLI files in npm package (duh)
## 0.5.0 ## 0.5.0
* Command line interface * Command line interface

15
README.md

@ -91,14 +91,13 @@ This is not a trivial task. There are almost certainly a great many complex edge
The example below is aspirational. It isn't yet implemented - it exists in the name of [README driven development](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html). The example below is aspirational. It isn't yet implemented - it exists in the name of [README driven development](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html).
```js ```js
rollup.rollup( 'app.js', { rollup.rollup({
// Override the default path resolution // The bundle's starting point
resolvePath: function ( importee, importer ) { entry: 'app.js',
// return a string or a falsy value - if falsy,
// import is kept external to the bundle. // Any external modules you don't want to include
// Alternative, return a Promise that fulfils // in the bundle (includes node built-ins)
// with a string or falsy value external: [ 'path', 'fs', 'some-other-lib' ]
}
}).then( function ( bundle ) { }).then( function ( bundle ) {
// generate code and a sourcemap // generate code and a sourcemap
const { code, map } = bundle.generate({ const { code, map } = bundle.generate({

7
bin/runRollup.js

@ -33,13 +33,16 @@ function bundle ( options, method ) {
handleError({ code: 'MISSING_INPUT_OPTION' }); handleError({ code: 'MISSING_INPUT_OPTION' });
} }
return rollup.rollup( options.input ).then( function ( bundle ) { return rollup.rollup({
entry: options.input
}).then( function ( bundle ) {
var generateOptions = { var generateOptions = {
dest: options.output,
format: options.format format: options.format
}; };
if ( options.output ) { if ( options.output ) {
return bundle.write( options.output, generateOptions ); return bundle.write( generateOptions );
} }
if ( options.sourcemap && options.sourcemap !== 'inline' ) { if ( options.sourcemap && options.sourcemap !== 'inline' ) {

5
package.json

@ -1,6 +1,6 @@
{ {
"name": "rollup", "name": "rollup",
"version": "0.5.0", "version": "0.6.4",
"description": "Next-generation ES6 module bundler", "description": "Next-generation ES6 module bundler",
"main": "dist/rollup.js", "main": "dist/rollup.js",
"jsnext:main": "src/rollup.js", "jsnext:main": "src/rollup.js",
@ -37,7 +37,7 @@
"gobble-babel": "^5.1.0", "gobble-babel": "^5.1.0",
"gobble-cli": "^0.4.2", "gobble-cli": "^0.4.2",
"gobble-esperanto-bundle": "^0.2.0", "gobble-esperanto-bundle": "^0.2.0",
"gobble-rollup": "^0.1.1", "gobble-rollup": "^0.2.0",
"mocha": "^2.2.4", "mocha": "^2.2.4",
"source-map-support": "^0.2.10", "source-map-support": "^0.2.10",
"source-map": "^0.1.40" "source-map": "^0.1.40"
@ -52,6 +52,7 @@
"files": [ "files": [
"src", "src",
"dist", "dist",
"bin",
"README.md" "README.md"
] ]
} }

20
src/Bundle.js

@ -6,7 +6,9 @@ import Module from './Module';
import ExternalModule from './ExternalModule'; import ExternalModule from './ExternalModule';
import finalisers from './finalisers/index'; import finalisers from './finalisers/index';
import makeLegalIdentifier from './utils/makeLegalIdentifier'; import makeLegalIdentifier from './utils/makeLegalIdentifier';
import { defaultResolver } from './utils/resolvePath'; import ensureArray from './utils/ensureArray';
import { defaultResolver, defaultExternalResolver } from './utils/resolvePath';
import { defaultLoader } from './utils/load';
function badExports ( option, keys ) { function badExports ( option, keys ) {
throw new Error( `'${option}' was specified for options.exports, but entry module has following exports: ${keys.join(', ')}` ); throw new Error( `'${option}' was specified for options.exports, but entry module has following exports: ${keys.join(', ')}` );
@ -18,17 +20,26 @@ export default class Bundle {
this.base = dirname( this.entryPath ); this.base = dirname( this.entryPath );
this.resolvePath = options.resolvePath || defaultResolver; this.resolvePath = options.resolvePath || defaultResolver;
this.load = options.load || defaultLoader;
this.resolvePathOptions = {
external: ensureArray( options.external ),
resolveExternal: options.resolveExternal || defaultExternalResolver
};
this.loadOptions = {
transform: ensureArray( options.transform )
};
this.entryModule = null; this.entryModule = null;
this.modulePromises = {}; this.modulePromises = {};
this.statements = []; this.statements = [];
this.externalModules = []; this.externalModules = [];
this.defaultExportName = null;
this.internalNamespaceModules = []; this.internalNamespaceModules = [];
} }
fetchModule ( importee, importer ) { fetchModule ( importee, importer ) {
return Promise.resolve( importer === null ? importee : this.resolvePath( importee, importer ) ) return Promise.resolve( importer === null ? importee : this.resolvePath( importee, importer, this.resolvePathOptions ) )
.then( path => { .then( path => {
if ( !path ) { if ( !path ) {
// external module // external module
@ -42,7 +53,7 @@ export default class Bundle {
} }
if ( !has( this.modulePromises, path ) ) { if ( !has( this.modulePromises, path ) ) {
this.modulePromises[ path ] = readFile( path, { encoding: 'utf-8' }) this.modulePromises[ path ] = Promise.resolve( this.load( path, this.loadOptions ) )
.then( source => { .then( source => {
const module = new Module({ const module = new Module({
path, path,
@ -85,7 +96,6 @@ export default class Bundle {
this.statements = statements; this.statements = statements;
this.deconflict(); this.deconflict();
}); });
} }
deconflict () { deconflict () {

2
src/Module.js

@ -19,7 +19,6 @@ export default class Module {
this.bundle = bundle; this.bundle = bundle;
this.path = path; this.path = path;
this.relativePath = relative( bundle.base, path ).slice( 0, -3 ); // remove .js
this.magicString = new MagicString( source, { this.magicString = new MagicString( source, {
filename: path filename: path
@ -40,6 +39,7 @@ export default class Module {
walk( ast, { walk( ast, {
enter: node => { enter: node => {
this.magicString.addSourcemapLocation( node.start ); this.magicString.addSourcemapLocation( node.start );
this.magicString.addSourcemapLocation( node.end );
} }
}); });

15
src/Statement.js

@ -156,17 +156,30 @@ export default class Statement {
checkForWrites ( scope, node ) { checkForWrites ( scope, node ) {
const addNode = ( node, disallowImportReassignments ) => { const addNode = ( node, disallowImportReassignments ) => {
let depth = 0; // determine whether we're illegally modifying a binding or namespace
while ( node.type === 'MemberExpression' ) { while ( node.type === 'MemberExpression' ) {
node = node.object; node = node.object;
depth += 1;
} }
// disallow assignments/updates to imported bindings and namespaces // disallow assignments/updates to imported bindings and namespaces
if ( disallowImportReassignments && has( this.module.imports, node.name ) && !scope.contains( node.name ) ) { if ( disallowImportReassignments ) {
const importSpecifier = this.module.imports[ node.name ];
if ( importSpecifier && !scope.contains( node.name ) ) {
const minDepth = importSpecifier.name === '*' ?
2 : // cannot do e.g. `namespace.foo = bar`
1; // cannot do e.g. `foo = bar`, but `foo.bar = bar` is fine
if ( depth < minDepth ) {
const err = new Error( `Illegal reassignment to import '${node.name}'` ); const err = new Error( `Illegal reassignment to import '${node.name}'` );
err.file = this.module.path; err.file = this.module.path;
err.loc = getLocation( this.module.magicString.toString(), node.start ); err.loc = getLocation( this.module.magicString.toString(), node.start );
throw err; throw err;
} }
}
}
if ( node.type !== 'Identifier' ) { if ( node.type !== 'Identifier' ) {
return; return;

7
src/finalisers/amd.js

@ -23,13 +23,16 @@ export default function amd ( bundle, magicString, exportMode, options ) {
if ( exportMode === 'default' ) { if ( exportMode === 'default' ) {
exportBlock = `return ${bundle.entryModule.getCanonicalName('default')};`; exportBlock = `return ${bundle.entryModule.getCanonicalName('default')};`;
} else { } else {
exportBlock = '\n\n' + Object.keys( exports ).map( name => { exportBlock = Object.keys( exports ).map( name => {
return `exports.${name} = ${exports[name].localName};`; return `exports.${name} = ${exports[name].localName};`;
}).join( '\n' ); }).join( '\n' );
} }
if ( exportBlock ) {
magicString.append( '\n\n' + exportBlock );
}
return magicString return magicString
.append( exportBlock )
.trim() .trim()
.indent() .indent()
.append( '\n\n});' ) .append( '\n\n});' )

25
src/finalisers/es6.js

@ -1,7 +1,26 @@
import { keys } from '../utils/object';
export default function es6 ( bundle, magicString, exportMode, options ) { export default function es6 ( bundle, magicString, exportMode, options ) {
// TODO const introBlock = ''; // TODO...
const introBlock = '';
const exportBlock = ''; const exports = bundle.entryModule.exports;
const exportBlock = keys( exports ).map( exportedName => {
const specifier = exports[ exportedName ];
const canonicalName = bundle.entryModule.getCanonicalName( specifier.localName );
if ( exportedName === 'default' ) {
return `export default ${canonicalName};`;
}
return exportedName === canonicalName ?
`export { ${exportedName} };` :
`export { ${canonicalName} as ${exportedName} };`;
}).join( '\n' );
if ( exportBlock ) {
magicString.append( '\n\n' + exportBlock );
}
return magicString.trim(); return magicString.trim();
} }

29
src/finalisers/iife.js

@ -1,10 +1,33 @@
import { has } from '../utils/object';
import { getName } from '../utils/map-helpers';
export default function iife ( bundle, magicString, exportMode, options ) { export default function iife ( bundle, magicString, exportMode, options ) {
const globalNames = options.globals || {};
let dependencies = bundle.externalModules.map( module => {
return has( globalNames, module.id ) ? globalNames[ module.id ] : module.name;
});
let args = bundle.externalModules.map( getName );
if ( exportMode !== 'none' && !options.moduleName ) {
throw new Error( 'You must supply options.moduleName for IIFE bundles' );
}
const intro = `(function () { 'use strict';\n\n`; if ( exportMode === 'named' ) {
const outro = `\n\n})();`; dependencies.unshift( `(window.${options.moduleName} = {})` );
args.unshift( 'exports' );
}
let intro = `(function (${args}) { 'use strict';\n\n`;
let outro = `\n\n})(${dependencies});`;
if ( exportMode === 'default' ) {
intro = `var ${options.moduleName} = ${intro}`;
magicString.append( `\n\nreturn ${bundle.entryModule.getCanonicalName('default')};` );
}
return magicString return magicString
.trim()
.indent() .indent()
.prepend( intro ) .prepend( intro )
.append( outro ); .append( outro );

24
src/finalisers/umd.js

@ -26,23 +26,37 @@ export default function umd ( bundle, magicString, exportMode, options ) {
( has( options, 'moduleId' ) ? `['${options.moduleId}'], ` : `` ) + ( has( options, 'moduleId' ) ? `['${options.moduleId}'], ` : `` ) +
( amdDeps.length ? `[${amdDeps.join( ', ' )}], ` : `` ); ( amdDeps.length ? `[${amdDeps.join( ', ' )}], ` : `` );
const cjsExport = exportMode === 'default' ? `module.exports = ` : ``;
const defaultExport = exportMode === 'default' ? `global.${options.moduleName} = ` : '';
const intro = const intro =
`(function (global, factory) { `(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(${cjsDeps.join( ', ' )}) : typeof exports === 'object' && typeof module !== 'undefined' ? ${cjsExport}factory(${cjsDeps.join( ', ' )}) :
typeof define === 'function' && define.amd ? define(${amdParams}factory) : typeof define === 'function' && define.amd ? define(${amdParams}factory) :
factory(${globalDeps}); ${defaultExport}factory(${globalDeps});
}(this, function (${args}) { 'use strict'; }(this, function (${args}) { 'use strict';
`.replace( /^\t\t/gm, '' ).replace( /^\t/gm, indentStr ); `.replace( /^\t\t/gm, '' ).replace( /^\t/gm, indentStr );
const exports = bundle.entryModule.exports; const exports = bundle.entryModule.exports;
const exportBlock = '\n\n' + Object.keys( exports ).map( name => { let exportBlock;
return `exports.${name} = ${exports[name].localName};`;
if ( exportMode === 'default' ) {
const canonicalName = bundle.entryModule.getCanonicalName( 'default' );
exportBlock = `return ${canonicalName};`;
} else {
exportBlock = Object.keys( exports ).map( name => {
const canonicalName = bundle.entryModule.getCanonicalName( exports[ name ].localName );
return `exports.${name} = ${canonicalName};`;
}).join( '\n' ); }).join( '\n' );
}
if ( exportBlock ) {
magicString.append( '\n\n' + exportBlock );
}
return magicString return magicString
.append( exportBlock )
.trim() .trim()
.indent() .indent()
.append( '\n\n}));' ) .append( '\n\n}));' )

32
src/rollup.js

@ -5,28 +5,25 @@ import Bundle from './Bundle';
let SOURCEMAPPING_URL = 'sourceMa'; let SOURCEMAPPING_URL = 'sourceMa';
SOURCEMAPPING_URL += 'ppingURL'; SOURCEMAPPING_URL += 'ppingURL';
export function rollup ( entry, options = {} ) { export function rollup ( options ) {
const bundle = new Bundle({ if ( !options || !options.entry ) {
entry, throw new Error( 'You must supply options.entry to rollup' );
resolvePath: options.resolvePath }
});
const bundle = new Bundle( options );
return bundle.build().then( () => { return bundle.build().then( () => {
return { return {
generate: options => bundle.generate( options ), generate: options => bundle.generate( options ),
write: ( dest, options = {} ) => { write: options => {
let { code, map } = bundle.generate({ if ( !options || !options.dest ) {
dest, throw new Error( 'You must supply options.dest to bundle.write' );
format: options.format, }
globalName: options.globalName,
const dest = options.dest;
// sourcemap options let { code, map } = bundle.generate( options );
sourceMap: !!options.sourceMap,
sourceMapFile: options.sourceMapFile,
// sourceMapRoot: options.sourceMapRoot
});
let promises = [ writeFile( dest, code ) ]; let promises = [];
if ( options.sourceMap ) { if ( options.sourceMap ) {
let url; let url;
@ -41,6 +38,7 @@ export function rollup ( entry, options = {} ) {
code += `\n//# ${SOURCEMAPPING_URL}=${url}`; code += `\n//# ${SOURCEMAPPING_URL}=${url}`;
} }
promises.push( writeFile( dest, code ) );
return Promise.all( promises ); return Promise.all( promises );
} }
}; };

5
src/utils/ensureArray.js

@ -0,0 +1,5 @@
export default function ensureArray ( thing ) {
if ( Array.isArray( thing ) ) return thing;
if ( thing == undefined ) return [];
return [ thing ];
}

10
src/utils/load.js

@ -0,0 +1,10 @@
import { readFileSync } from 'sander';
export function defaultLoader ( path, options ) {
// TODO support plugins e.g. !css and !json?
const source = readFileSync( path, { encoding: 'utf-8' });
return options.transform.reduce( ( source, transformer ) => {
return transformer( source, path );
}, source );
}

53
src/utils/resolvePath.js

@ -1,11 +1,56 @@
import { dirname, isAbsolute, resolve } from 'path'; import { dirname, isAbsolute, resolve, parse } from 'path';
import { readFileSync } from 'sander';
export function defaultResolver ( importee, importer ) { export function defaultResolver ( importee, importer, options ) {
// absolute paths are left untouched // absolute paths are left untouched
if ( isAbsolute( importee ) ) return importee; if ( isAbsolute( importee ) ) return importee;
// external modules stay external // we try to resolve external modules
if ( importee[0] !== '.' ) return false; if ( importee[0] !== '.' ) {
// unless we want to keep it external, that is
if ( ~options.external.indexOf( importee ) ) return null;
return options.resolveExternal( importee, importer, options );
}
return resolve( dirname( importer ), importee ).replace( /\.js$/, '' ) + '.js'; return resolve( dirname( importer ), importee ).replace( /\.js$/, '' ) + '.js';
} }
export function defaultExternalResolver ( id, importer, options ) {
// for now, only node_modules is supported, and only jsnext:main
let parsed = parse( importer );
let dir = parsed.dir;
while ( dir !== parsed.root ) {
const pkgPath = resolve( dir, 'node_modules', id, 'package.json' );
let pkgJson;
try {
pkgJson = readFileSync( pkgPath ).toString();
} catch ( err ) {
// noop
}
if ( pkgJson ) {
let pkg;
try {
pkg = JSON.parse( pkgJson );
} catch ( err ) {
throw new Error( `Malformed JSON: ${pkgPath}` );
}
const main = pkg[ 'jsnext:main' ];
if ( !main ) {
throw new Error( `Package ${id} does not have a jsnext:main field, and so cannot be included in your rollup. Try adding it as an external module instead (e.g. options.external = ['${id}']). See https://github.com/rollup/rollup/wiki/jsnext:main for more info` );
}
return resolve( dirname( pkgPath ), main ).replace( /\.js$/, '' ) + '.js';
}
dir = dirname( dir );
}
throw new Error( `Could not find package ${id} (required by ${importer})` );
}

Loading…
Cancel
Save