diff --git a/packages/core/core/src/requests/PathRequest.js b/packages/core/core/src/requests/PathRequest.js index 27ef8da5ce1..65b33fe280b 100644 --- a/packages/core/core/src/requests/PathRequest.js +++ b/packages/core/core/src/requests/PathRequest.js @@ -158,19 +158,21 @@ export class ResolverRunner { logger: new PluginLogger({origin: resolver.name}), }); - if (result && result.isExcluded) { - return null; - } - - if (result?.filePath != null) { - return { - filePath: result.filePath, - sideEffects: result.sideEffects, - code: result.code, - env: dependency.env, - pipeline: pipeline ?? dependency.pipeline, - isURL: dependency.isURL, - }; + if (result) { + if (result.isExcluded) { + return null; + } + + if (result.filePath != null) { + return { + filePath: result.filePath, + sideEffects: result.sideEffects, + code: result.code, + env: dependency.env, + pipeline: pipeline ?? dependency.pipeline, + isURL: dependency.isURL, + }; + } } } catch (e) { // Add error to error map, we'll append these to the standard error if we can't resolve the asset diff --git a/packages/core/integration-tests/test/globals.js b/packages/core/integration-tests/test/globals.js new file mode 100644 index 00000000000..a7b849afa52 --- /dev/null +++ b/packages/core/integration-tests/test/globals.js @@ -0,0 +1,22 @@ +import assert from 'assert'; +import path from 'path'; +import {bundle, run} from '@parcel/test-utils'; + +describe('global alias', function() { + it('should support global alias syntax', async function() { + let b = await bundle( + path.join(__dirname, '/integration/global-alias/index.js'), + ); + + assert.equal( + await run(b, { + React: { + createElement: function() { + return 'ok'; + }, + }, + }), + 'ok', + ); + }); +}); diff --git a/packages/core/integration-tests/test/integration/global-alias/index.js b/packages/core/integration-tests/test/integration/global-alias/index.js new file mode 100644 index 00000000000..832e891097b --- /dev/null +++ b/packages/core/integration-tests/test/integration/global-alias/index.js @@ -0,0 +1,3 @@ +import React from 'react'; + +module.exports = React.createElement('div'); \ No newline at end of file diff --git a/packages/core/integration-tests/test/integration/global-alias/package.json b/packages/core/integration-tests/test/integration/global-alias/package.json new file mode 100644 index 00000000000..8a5c0216f55 --- /dev/null +++ b/packages/core/integration-tests/test/integration/global-alias/package.json @@ -0,0 +1,6 @@ +{ + "main": "dist/index.js", + "alias": { + "react": { "global": "React" } + } +} \ No newline at end of file diff --git a/packages/core/parcel/README.md b/packages/core/parcel/README.md index 339ae0619c2..39f0ed1018e 100644 --- a/packages/core/parcel/README.md +++ b/packages/core/parcel/README.md @@ -22,6 +22,14 @@ - 🔥 Built in support for **hot module replacement** - 🚨 Friendly error logging experience - syntax highlighted code frames help pinpoint the problem. + +--- + +**Below is the design document that was created before work on the implementation of Parcel 2 started and some sections are outdated. The actual (somewhat complete) documentation for Parcel 2 is available here: https://v2.parceljs.org/.** + + +---- + ## Getting Started Before we get started, you'll need to install Node and Yarn (or npm) and create diff --git a/packages/utils/node-resolver-core/src/NodeResolver.js b/packages/utils/node-resolver-core/src/NodeResolver.js index f5d72370f21..5e16b5c5821 100644 --- a/packages/utils/node-resolver-core/src/NodeResolver.js +++ b/packages/utils/node-resolver-core/src/NodeResolver.js @@ -46,6 +46,7 @@ type Module = {| subPath?: string, moduleDir?: FilePath, filePath?: FilePath, + code?: string, |}; /** @@ -110,6 +111,12 @@ export default class NodeResolver { if (module.moduleDir) { resolved = await this.loadNodeModules(module, extensions, env); } else if (module.filePath) { + if (module.code != null) { + return { + filePath: module.filePath, + code: module.code, + }; + } resolved = await this.loadRelative( module.filePath, extensions, @@ -266,7 +273,18 @@ export default class NodeResolver { } // Resolve aliases in the parent module for this file. - filename = await this.loadAlias(filename, dir, env); + let alias = await this.loadAlias(filename, dir, env); + if (Array.isArray(alias)) { + if (alias[0] === 'global') { + return { + filePath: `${alias[1]}.js`, + code: `module.exports=${alias[1]};`, + }; + } + filename = alias[1]; + } else if (typeof alias === 'string') { + filename = alias; + } // Return just the file path if this is a file, not in node_modules if (path.isAbsolute(filename)) { @@ -747,7 +765,16 @@ export default class NodeResolver { let f = file + ext; if (expandAliases) { - let alias = await this.resolveAliases(file + ext, env, pkg); + let alias = await this.resolveAliases(f, env, pkg); + if (Array.isArray(alias)) { + if (alias[0] === 'global') { + alias = f; + } else { + alias = alias[1]; + } + } else if (alias === false) { + alias = f; + } if (alias !== f) { res = res.concat( await this.expandFile(alias, extensions, env, pkg, false), @@ -766,12 +793,13 @@ export default class NodeResolver { env: Environment, pkg: InternalPackageJSON | null, ) { + let localAliases = await this.resolvePackageAliases(filename, env, pkg); + if (Array.isArray(localAliases) || typeof localAliases === 'boolean') { + return localAliases; + } + // First resolve local package aliases, then project global ones. - return this.resolvePackageAliases( - await this.resolvePackageAliases(filename, env, pkg), - env, - this.rootPackage, - ); + return this.resolvePackageAliases(filename, env, this.rootPackage); } async resolvePackageAliases( @@ -783,14 +811,14 @@ export default class NodeResolver { return filename; } - // Resolve aliases in the package.source, package.alias, and package.browser fields. - return ( - (await this.getAlias(filename, pkg.pkgdir, pkg.source)) || - (await this.getAlias(filename, pkg.pkgdir, pkg.alias)) || - (env.isBrowser() && - (await this.getAlias(filename, pkg.pkgdir, pkg.browser))) || - filename - ); + let pkgKeys = ['source', 'alias']; + if (env.isBrowser()) pkgKeys.push('browser'); + + for (let pkgKey of pkgKeys) { + let alias = await this.getAlias(filename, pkg.pkgdir, pkg[pkgKey]); + if (alias != null) return alias; + } + return filename; } async getAlias(filename: FilePath, dir: FilePath, aliases: ?Aliases) { @@ -803,18 +831,14 @@ export default class NodeResolver { // If filename is an absolute path, get one relative to the package.json directory. if (path.isAbsolute(filename)) { filename = relativePath(dir, filename); - alias = await this.lookupAlias(aliases, filename, dir); + alias = await this.lookupAlias(aliases, filename); } else { // It is a node_module. First try the entire filename as a key. - alias = await this.lookupAlias( - aliases, - normalizeSeparators(filename), - dir, - ); + alias = await this.lookupAlias(aliases, normalizeSeparators(filename)); if (alias == null) { // If it didn't match, try only the module name. let [moduleName, subPath] = this.getModuleParts(filename); - alias = await this.lookupAlias(aliases, moduleName, dir); + alias = await this.lookupAlias(aliases, moduleName); if (typeof alias === 'string' && subPath) { // Append the filename back onto the aliased module. alias = path.join(alias, subPath); @@ -824,13 +848,34 @@ export default class NodeResolver { // If the alias is set to `false`, return an empty file. if (alias === false) { - return EMPTY_SHIM; + return ['file', EMPTY_SHIM]; } - return alias; + if (alias instanceof Object) { + if (alias.global) { + if (typeof alias.global !== 'string' || alias.global.length === 0) { + throw new ThrowableDiagnostic({ + diagnostic: { + message: `The global alias for ${filename} is invalid.`, + hints: [`Only nonzero-length strings are valid global aliases.`], + }, + }); + } + return ['global', alias.global]; + } else if (alias.fileName) { + alias = alias.fileName; + } + } + + if (typeof alias === 'string') { + // Assume file + return ['file', await this.resolveFilename(alias, dir)]; + } + + return null; } - lookupAlias(aliases: Aliases, filename: FilePath, dir: FilePath) { + lookupAlias(aliases: Aliases, filename: FilePath) { if (typeof aliases !== 'object') { return null; } @@ -850,14 +895,7 @@ export default class NodeResolver { } } } - - if (typeof alias === 'string') { - return this.resolveFilename(alias, dir); - } else if (alias === false) { - return false; - } - - return null; + return alias; } async findPackage(dir: string) {