1 | import { statSync } from 'fs';
|
2 | import { dirname, extname, resolve, sep } from 'path';
|
3 | import { sync as nodeResolveSync } from 'resolve';
|
4 | import { createFilter } from 'rollup-pluginutils';
|
5 | import { EXTERNAL, PREFIX, HELPERS_ID, HELPERS } from './helpers.js';
|
6 | import defaultResolver from './defaultResolver.js';
|
7 | import { checkFirstpass, checkEsModule, transformCommonjs } from './transform.js';
|
8 | import { getName } from './utils.js';
|
9 |
|
10 | function getCandidatesForExtension ( resolved, extension ) {
|
11 | return [
|
12 | resolved + extension,
|
13 | resolved + `${sep}index${extension}`
|
14 | ];
|
15 | }
|
16 |
|
17 | function getCandidates ( resolved, extensions ) {
|
18 | return extensions.reduce(
|
19 | ( paths, extension ) => paths.concat( getCandidatesForExtension ( resolved, extension ) ),
|
20 | [resolved]
|
21 | );
|
22 | }
|
23 |
|
24 |
|
25 |
|
26 | function 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 |
|
36 | function startsWith ( str, prefix ) {
|
37 | return str.slice( 0, prefix.length ) === prefix;
|
38 | }
|
39 |
|
40 | const isCjsPromises = Object.create(null);
|
41 |
|
42 | export 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;
|
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;
|
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 |
|
140 | return ( importee, importer ) => {
|
141 | if ( importee[0] !== '.' || !importer ) return;
|
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 ) { }
|
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 |
|
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 |
|
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 | }
|