UNPKG

7.1 kBJavaScriptView Raw
1import { statSync } from 'fs';
2import { dirname, extname, resolve, sep } from 'path';
3import { sync as nodeResolveSync } from 'resolve';
4import { createFilter } from 'rollup-pluginutils';
5import { EXTERNAL, PREFIX, HELPERS_ID, HELPERS } from './helpers.js';
6import defaultResolver from './defaultResolver.js';
7import { checkFirstpass, checkEsModule, transformCommonjs } from './transform.js';
8import { getName } from './utils.js';
9
10function getCandidatesForExtension ( resolved, extension ) {
11 return [
12 resolved + extension,
13 resolved + `${sep}index${extension}`
14 ];
15}
16
17function getCandidates ( resolved, extensions ) {
18 return extensions.reduce(
19 ( paths, extension ) => paths.concat( getCandidatesForExtension ( resolved, extension ) ),
20 [resolved]
21 );
22}
23
24// Return the first non-falsy result from an array of
25// maybe-sync, maybe-promise-returning functions
26function first ( candidates ) {
27 return function ( ...args ) {
28 return candidates.reduce( ( promise, candidate ) => {
29 return promise.then( result => result != null ?
30 result :
31 Promise.resolve( candidate( ...args ) ) );
32 }, Promise.resolve() );
33 };
34}
35
36function startsWith ( str, prefix ) {
37 return str.slice( 0, prefix.length ) === prefix;
38}
39
40const isCjsPromises = Object.create(null);
41
42export default function commonjs ( options = {} ) {
43 const extensions = options.extensions || ['.js'];
44 const filter = createFilter( options.include, options.exclude );
45 const ignoreGlobal = options.ignoreGlobal;
46
47 const customNamedExports = {};
48 if ( options.namedExports ) {
49 Object.keys( options.namedExports ).forEach( id => {
50 let resolvedId;
51
52 try {
53 resolvedId = nodeResolveSync( id, { basedir: process.cwd() });
54 } catch ( err ) {
55 resolvedId = resolve( id );
56 }
57
58 customNamedExports[ resolvedId ] = options.namedExports[ id ];
59 });
60 }
61
62 const esModulesWithoutDefaultExport = [];
63
64 const allowDynamicRequire = !!options.ignore; // TODO maybe this should be configurable?
65
66 const ignoreRequire = typeof options.ignore === 'function' ?
67 options.ignore :
68 Array.isArray( options.ignore ) ? id => ~options.ignore.indexOf( id ) :
69 () => false;
70
71 let entryModuleIdsPromise = null;
72
73 function resolveId ( importee, importer ) {
74 const isProxyModule = startsWith( importee, PREFIX );
75 if ( isProxyModule ) {
76 importee = importee.slice( PREFIX.length );
77 }
78 else if ( startsWith( importee, '\0' ) ) {
79 return importee;
80 }
81
82 if ( importer && startsWith( importer, PREFIX ) ) {
83 importer = importer.slice( PREFIX.length );
84 }
85
86 return resolveUsingOtherResolvers( importee, importer ).then( resolved => {
87 if ( resolved ) return isProxyModule ? PREFIX + resolved : resolved;
88
89 resolved = defaultResolver( importee, importer );
90
91 if ( isProxyModule ) {
92 if ( resolved ) return PREFIX + resolved;
93 return EXTERNAL + importee; // external
94 }
95
96 return resolved;
97 });
98 }
99
100 const sourceMap = options.sourceMap !== false;
101
102 let resolveUsingOtherResolvers;
103
104 function getIsCjsPromise ( id ) {
105 let isCjsPromise = isCjsPromises[id];
106 if (isCjsPromise)
107 return isCjsPromise.promise;
108
109 const promise = new Promise( resolve => {
110 isCjsPromises[id] = isCjsPromise = {
111 resolve: resolve,
112 promise: undefined
113 };
114 });
115 isCjsPromise.promise = promise;
116
117 return promise;
118 }
119 function setIsCjsPromise ( id, promise ) {
120 const isCjsPromise = isCjsPromises[id];
121 if (isCjsPromise) {
122 if (isCjsPromise.resolve) {
123 isCjsPromise.resolve(promise);
124 isCjsPromise.resolve = undefined;
125 }
126 }
127 else {
128 isCjsPromises[id] = { promise: promise, resolve: undefined };
129 }
130 }
131
132 return {
133 name: 'commonjs',
134
135 options ( options ) {
136 const resolvers = ( options.plugins || [] )
137 .map( plugin => {
138 if ( plugin.resolveId === resolveId ) {
139 // substitute CommonJS resolution logic
140 return ( importee, importer ) => {
141 if ( importee[0] !== '.' || !importer ) return; // not our problem
142
143 const resolved = resolve( dirname( importer ), importee );
144 const candidates = getCandidates( resolved, extensions );
145
146 for ( let i = 0; i < candidates.length; i += 1 ) {
147 try {
148 const stats = statSync( candidates[i] );
149 if ( stats.isFile() ) return candidates[i];
150 } catch ( err ) { /* noop */ }
151 }
152 };
153 }
154
155 return plugin.resolveId;
156 })
157 .filter( Boolean );
158
159 const isExternal = id => options.external ?
160 Array.isArray( options.external ) ? ~options.external.indexOf( id ) :
161 options.external(id) :
162 false;
163
164 resolvers.unshift( id => isExternal( id ) ? false : null );
165
166 resolveUsingOtherResolvers = first( resolvers );
167 const input = options.input || options.entry;
168 const entryModules = Array.isArray(input) ?
169 input :
170 typeof input === 'object' && input !== null ?
171 Object.values(input) :
172 [input];
173 entryModuleIdsPromise = Promise.all(
174 entryModules.map( entry => resolveId( entry ))
175 );
176 },
177
178 resolveId,
179
180 load ( id ) {
181 if ( id === HELPERS_ID ) return HELPERS;
182
183 // generate proxy modules
184 if ( startsWith( id, EXTERNAL ) ) {
185 const actualId = id.slice( EXTERNAL.length );
186 const name = getName( actualId );
187
188 return `import ${name} from ${JSON.stringify( actualId )}; export default ${name};`;
189 }
190
191 if ( startsWith( id, PREFIX ) ) {
192 const actualId = id.slice( PREFIX.length );
193 const name = getName( actualId );
194
195 return ( ( extensions.indexOf( extname( id ) ) === -1 ) ? Promise.resolve(false) : getIsCjsPromise( actualId ) )
196 .then( isCjs => {
197 if ( isCjs )
198 return `import { __moduleExports } from ${JSON.stringify( actualId )}; export default __moduleExports;`;
199 else if (esModulesWithoutDefaultExport.indexOf(actualId) !== -1)
200 return `import * as ${name} from ${JSON.stringify( actualId )}; export default ${name};`;
201 else
202 return `import * as ${name} from ${JSON.stringify( actualId )}; export default ( ${name} && ${name}['default'] ) || ${name};`;
203 });
204 }
205 },
206
207 transform ( code, id ) {
208 if ( !filter( id ) ) return null;
209 if ( extensions.indexOf( extname( id ) ) === -1 ) return null;
210
211 const transformPromise = entryModuleIdsPromise.then( (entryModuleIds) => {
212 const {isEsModule, hasDefaultExport, ast} = checkEsModule( this.parse, code, id );
213 if ( isEsModule ) {
214 if ( !hasDefaultExport )
215 esModulesWithoutDefaultExport.push( id );
216 return;
217 }
218
219 // it is not an ES module but not a commonjs module, too.
220 if ( !checkFirstpass( code, ignoreGlobal ) ) {
221 esModulesWithoutDefaultExport.push( id );
222 return;
223 }
224
225 const transformed = transformCommonjs( this.parse, code, id, entryModuleIds.indexOf(id) !== -1, ignoreGlobal, ignoreRequire, customNamedExports[ id ], sourceMap, allowDynamicRequire, ast );
226 if ( !transformed ) {
227 esModulesWithoutDefaultExport.push( id );
228 return;
229 }
230
231 return transformed;
232 }).catch(err => {
233 this.error(err, err.loc);
234 });
235
236 setIsCjsPromise(id, transformPromise.then( transformed => transformed ? true : false, () => true ));
237
238 return transformPromise;
239 }
240 };
241}