From 51c2a99b7b3bdd7b66e83ebba808177e7d920955 Mon Sep 17 00:00:00 2001 From: cmorten Date: Sun, 24 Jan 2021 11:54:08 +0000 Subject: [PATCH 1/5] fix: remove isolated resolve() This is incompatible with the browser implementation of the method. --- src/utils/resolveId.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/resolveId.ts b/src/utils/resolveId.ts index 86cdce38ce2..5dc6566b525 100644 --- a/src/utils/resolveId.ts +++ b/src/utils/resolveId.ts @@ -28,7 +28,7 @@ export async function resolveId( // resolve call and require no special handing on our part. // See https://nodejs.org/api/path.html#path_path_resolve_paths return addJsExtensionIfNecessary( - resolve(importer ? dirname(importer) : resolve(), source), + importer ? resolve(dirname(importer), source) : resolve(source), preserveSymlinks ); } From 4b8514a12f7d0295cfdd0d169cff4baf5210b178 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Wed, 27 Jan 2021 06:35:28 +0100 Subject: [PATCH 2/5] Set up basic browser tests --- .eslintrc.json | 81 +++++++++--------- .mocharc.json | 3 - browser/error.ts | 8 ++ browser/fs.ts | 16 +--- browser/resolveId.ts | 23 +++++ build-plugins/replace-browser-modules.js | 27 ++++++ package.json | 9 +- rollup.config.js | 9 +- test/browser/index.mjs | 83 +++++++++++++++++++ test/browser/samples/basic/_config.js | 13 +++ test/browser/samples/basic/_expected/main.js | 3 + .../missing-dependency-resolution/_config.js | 16 ++++ .../missing-entry-resolution/_config.js | 7 ++ test/browser/samples/missing-load/_config.js | 17 ++++ test/utils.js | 1 + 15 files changed, 251 insertions(+), 65 deletions(-) delete mode 100644 .mocharc.json create mode 100644 browser/error.ts create mode 100644 browser/resolveId.ts create mode 100644 build-plugins/replace-browser-modules.js create mode 100644 test/browser/index.mjs create mode 100644 test/browser/samples/basic/_config.js create mode 100644 test/browser/samples/basic/_expected/main.js create mode 100644 test/browser/samples/missing-dependency-resolution/_config.js create mode 100644 test/browser/samples/missing-entry-resolution/_config.js create mode 100644 test/browser/samples/missing-load/_config.js diff --git a/.eslintrc.json b/.eslintrc.json index 63fa0daa3aa..06e4f25029e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,43 +6,46 @@ "**/*.ts" ], "rules": { - "indent": [ 2, "tab", { "SwitchCase": 1 } ], - "semi": [ 2, "always" ], - "keyword-spacing": [ 2, { "before": true, "after": true } ], - "space-before-blocks": [ 2, "always" ], - "no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ], - "no-cond-assign": 0, - "no-unused-vars": 2, - "object-shorthand": [ 2, "always" ], - "no-const-assign": 2, - "no-class-assign": 2, - "no-this-before-super": 2, - "no-var": 2, - "no-unreachable": 2, - "valid-typeof": 2, - "quote-props": [ 2, "as-needed" ], - "one-var": [ 2, "never" ], - "prefer-arrow-callback": 2, - "prefer-const": [ 2, { "destructuring": "all" } ], - "arrow-spacing": 2 - }, - "env": { - "es6": true, - "browser": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:import/errors", - "plugin:import/warnings" - ], - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "settings": { - "import/ignore": [ 0, [ - "\\.path.js$" - ] ] - } + "indent": [2, "tab", { "SwitchCase": 1 }], + "semi": [2, "always"], + "keyword-spacing": [2, { "before": true, "after": true }], + "space-before-blocks": [2, "always"], + "no-mixed-spaces-and-tabs": [2, "smart-tabs"], + "no-cond-assign": 0, + "no-unused-vars": 2, + "object-shorthand": [2, "always"], + "no-const-assign": 2, + "no-class-assign": 2, + "no-this-before-super": 2, + "no-var": 2, + "no-unreachable": 2, + "valid-typeof": 2, + "quote-props": [2, "as-needed"], + "one-var": [2, "never"], + "prefer-arrow-callback": 2, + "prefer-const": [2, { "destructuring": "all" }], + "arrow-spacing": 2 + }, + "env": { + "es6": true, + "browser": true, + "node": true + }, + "extends": ["eslint:recommended", "plugin:import/errors", "plugin:import/warnings"], + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "settings": { + "import/ignore": [0, ["\\.path.js$"]] + }, + "overrides": [ + { + "files": ["./test/**"], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + } + } + ] } diff --git a/.mocharc.json b/.mocharc.json deleted file mode 100644 index 320d59dfe0e..00000000000 --- a/.mocharc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "spec": "test/test.js" -} diff --git a/browser/error.ts b/browser/error.ts new file mode 100644 index 00000000000..951e87cf362 --- /dev/null +++ b/browser/error.ts @@ -0,0 +1,8 @@ +export const throwNoFileSystem = (method: string) => (..._args: any[]): never => { + throw Object.assign( + new Error( + `Cannot access the file system (via "${method}") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.` + ), + { code: 'NO_FS_IN_BROWSER', url: 'https://rollupjs.org/guide/en/#a-simple-example' } + ); +}; diff --git a/browser/fs.ts b/browser/fs.ts index fbd18d02214..31337a88016 100644 --- a/browser/fs.ts +++ b/browser/fs.ts @@ -1,14 +1,4 @@ -const nope = (method: string) => (..._args: any[]): never => { - throw Object.assign( - new Error( - `Cannot access the file system (via "fs.${method}") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.` - ), - { code: 'NO_FS_IN_BROWSER', url: 'https://rollupjs.org/guide/en/#a-simple-example' } - ); -}; +import { throwNoFileSystem } from './error'; -export const lstatSync = nope('lstatSync'); -export const readdirSync = nope('readdirSync'); -export const readFile = nope('readFile'); -export const realpathSync = nope('realpathSync'); -export const writeFile = nope('writeFile'); +export const readFile = throwNoFileSystem('fs.readFile'); +export const writeFile = throwNoFileSystem('fs.writeFile'); diff --git a/browser/resolveId.ts b/browser/resolveId.ts new file mode 100644 index 00000000000..7b12816c0bf --- /dev/null +++ b/browser/resolveId.ts @@ -0,0 +1,23 @@ +import { CustomPluginOptions } from '../src/rollup/types'; +import { PluginDriver } from '../src/utils/PluginDriver'; +import { throwNoFileSystem } from './error'; + +export async function resolveId( + source: string, + importer: string | undefined, + _preserveSymlinks: boolean, + pluginDriver: PluginDriver, + skip: number | null, + customOptions: CustomPluginOptions | undefined +) { + const pluginResult = await pluginDriver.hookFirst( + 'resolveId', + [source, importer, { custom: customOptions }], + null, + skip + ); + if (pluginResult == null) { + throwNoFileSystem('path.resolve'); + } + return pluginResult; +} diff --git a/build-plugins/replace-browser-modules.js b/build-plugins/replace-browser-modules.js new file mode 100644 index 00000000000..9e004c5ffd9 --- /dev/null +++ b/build-plugins/replace-browser-modules.js @@ -0,0 +1,27 @@ +import path from 'path'; + +const ID_CRYPTO = path.resolve('src/utils/crypto'); +const ID_FS = path.resolve('src/utils/fs'); +const ID_PATH = path.resolve('src/utils/path'); +const ID_RESOLVEID = path.resolve('src/utils/resolveId'); + +export default function replaceBrowserModules() { + return { + name: 'replace-browser-modules', + resolveId: (source, importee) => { + if (importee && source[0] === '.') { + const resolved = path.join(path.dirname(importee), source); + switch (resolved) { + case ID_CRYPTO: + return path.resolve('browser/crypto.ts'); + case ID_FS: + return path.resolve('browser/fs.ts'); + case ID_PATH: + return path.resolve('browser/path.ts'); + case ID_RESOLVEID: + return path.resolve('browser/resolveId.ts'); + } + } + } + }; +} diff --git a/package.json b/package.json index 2bb427cd659..ed6c84f21d7 100644 --- a/package.json +++ b/package.json @@ -27,17 +27,20 @@ "prepare": "npm run build", "prepublishOnly": "npm ci && npm run lint:nofix && npm run security && npm run build:bootstrap && npm run test:all", "pretest": "npm run build:test", + "pretest:browser": "npm run build", "pretest:coverage": "npm run build:test && shx rm -rf coverage/*", "pretest:typescript": "shx rm -rf test/typescript/dist && shx cp -r dist test/typescript/", "security": "# npm audit # deactivated until there is a solution for the lodash issue", "test": "npm run test:all", "test:all": "npm run test:only && npm run test:typescript && npm run test:leak && npm run test:package", - "test:coverage": "nyc --reporter html mocha", + "test:coverage": "nyc --reporter html mocha test/test.js", "test:leak": "node --expose-gc test/leak/index.js", "test:package": "node scripts/test-package.js", - "test:only": "mocha", - "test:quick": "mocha -b", + "test:only": "mocha test/test.js", + "test:quick": "mocha -b test/test.js", "test:typescript": "tsc --noEmit -p test/typescript && tsc --noEmit", + "test:browser": "mocha test/browser/index.mjs", + "test:browser:quick": "mocha -b test/browser/index.mjs", "watch": "rollup -cw" }, "repository": "rollup/rollup", diff --git a/rollup.config.js b/rollup.config.js index fb27c72b742..eb18ad82c6f 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -12,6 +12,7 @@ import conditionalFsEventsImport from './build-plugins/conditional-fsevents-impo import emitModulePackageFile from './build-plugins/emit-module-package-file.js'; import esmDynamicImport from './build-plugins/esm-dynamic-import.js'; import getLicenseHandler from './build-plugins/generate-license-file'; +import replaceBrowserModules from './build-plugins/replace-browser-modules.js'; import pkg from './package.json'; const commitHash = (function () { @@ -142,16 +143,10 @@ export default command => { input: 'src/browser-entry.ts', onwarn, plugins: [ + replaceBrowserModules(), alias(moduleAliases), resolve({ browser: true }), json(), - { - load: id => { - if (~id.indexOf('crypto.ts')) return fs.readFileSync('browser/crypto.ts', 'utf-8'); - if (~id.indexOf('fs.ts')) return fs.readFileSync('browser/fs.ts', 'utf-8'); - if (~id.indexOf('path.ts')) return fs.readFileSync('browser/path.ts', 'utf-8'); - } - }, commonjs(), typescript(), terser({ module: true, output: { comments: 'some' } }), diff --git a/test/browser/index.mjs b/test/browser/index.mjs new file mode 100644 index 00000000000..84bd5cb93eb --- /dev/null +++ b/test/browser/index.mjs @@ -0,0 +1,83 @@ +import fixturify from 'fixturify'; +import { basename, dirname, resolve } from 'path'; +import { fileURLToPath } from 'url'; +import { rollup } from '../../dist/es/rollup.browser.js'; +import { assertFilesAreEqual, runTestSuiteWithSamples, compareError } from '../utils.js'; + +runTestSuiteWithSamples( + 'form', + resolve(dirname(fileURLToPath(import.meta.url)), 'samples'), + (dir, config) => { + (config.skip ? it.skip : config.solo ? it.only : it)( + basename(dir) + ': ' + config.description, + async () => { + let bundle; + try { + bundle = await rollup({ + input: 'main', + onwarn: warning => { + if ( + !(config.expectedWarnings && config.expectedWarnings.indexOf(warning.code) >= 0) + ) { + throw new Error( + `Unexpected warnings (${warning.code}): ${warning.message}\n` + + 'If you expect warnings, list their codes in config.expectedWarnings' + ); + } + }, + strictDeprecations: true, + ...config.options + }); + } catch (error) { + if (config.error) { + compareError(error, config.error); + return; + } else { + throw error; + } + } + if (config.error) { + throw new Error('Expected an error while rolling up'); + } + let output; + try { + ({ output } = await bundle.generate({ + exports: 'auto', + format: 'es', + ...(config.options || {}).output + })); + } catch (error) { + if (config.generateError) { + compareError(error, config.generateError); + return; + } else { + throw error; + } + } + if (config.generateError) { + throw new Error('Expected an error while generating output'); + } + assertOutputMatches(output, dir); + } + ); + } +); + +function assertOutputMatches(output, dir) { + const actual = {}; + for (const file of output) { + const filePath = file.fileName.split('/'); + const fileName = filePath.pop(); + let currentDir = actual; + for (const pathElement of filePath) { + if (!currentDir[pathElement]) { + currentDir[pathElement] = {}; + } + currentDir = currentDir[pathElement] = currentDir[pathElement] || {}; + } + currentDir[fileName] = file.source || file.code; + } + fixturify.writeSync(resolve(dir, '_actual'), actual); + const expected = fixturify.readSync(resolve(dir, '_expected')); + assertFilesAreEqual(actual, expected); +} diff --git a/test/browser/samples/basic/_config.js b/test/browser/samples/basic/_config.js new file mode 100644 index 00000000000..91c5821a80a --- /dev/null +++ b/test/browser/samples/basic/_config.js @@ -0,0 +1,13 @@ +const { loader } = require('../../../utils.js'); + +module.exports = { + show: true, + description: 'bundles files for the browser', + options: { + plugins: loader({ + main: `import {foo} from 'dep'; +console.log(foo);`, + dep: `export const foo = 42;` + }) + } +}; diff --git a/test/browser/samples/basic/_expected/main.js b/test/browser/samples/basic/_expected/main.js new file mode 100644 index 00000000000..a1865d00d26 --- /dev/null +++ b/test/browser/samples/basic/_expected/main.js @@ -0,0 +1,3 @@ +const foo = 42; + +console.log(foo); diff --git a/test/browser/samples/missing-dependency-resolution/_config.js b/test/browser/samples/missing-dependency-resolution/_config.js new file mode 100644 index 00000000000..123ab0a95e2 --- /dev/null +++ b/test/browser/samples/missing-dependency-resolution/_config.js @@ -0,0 +1,16 @@ +const { loader } = require('../../../utils.js'); + +module.exports = { + description: 'fails if a dependency cannot be resolved', + options: { + plugins: loader({ + main: `import {foo} from 'dep'; +console.log(foo);` + }) + }, + error: { + message: + "Unexpected warnings (UNRESOLVED_IMPORT): 'dep' is imported by main, but could not be resolved – treating it as an external dependency\nIf you expect warnings, list their codes in config.expectedWarnings", + watchFiles: ['main'] + } +}; diff --git a/test/browser/samples/missing-entry-resolution/_config.js b/test/browser/samples/missing-entry-resolution/_config.js new file mode 100644 index 00000000000..727dfa793a2 --- /dev/null +++ b/test/browser/samples/missing-entry-resolution/_config.js @@ -0,0 +1,7 @@ +module.exports = { + description: 'fails if an entry cannot be resolved', + error: { + code: 'UNRESOLVED_ENTRY', + message: 'Could not resolve entry module (main).' + } +}; diff --git a/test/browser/samples/missing-load/_config.js b/test/browser/samples/missing-load/_config.js new file mode 100644 index 00000000000..3cedcdce298 --- /dev/null +++ b/test/browser/samples/missing-load/_config.js @@ -0,0 +1,17 @@ +module.exports = { + description: 'fails if a file cannot be loaded', + options: { + plugins: { + resolveId(source) { + return source; + } + } + }, + error: { + code: 'NO_FS_IN_BROWSER', + message: + 'Could not load main: Cannot access the file system (via "fs.readFile") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.', + url: 'https://rollupjs.org/guide/en/#a-simple-example', + watchFiles: ['main'] + } +}; diff --git a/test/utils.js b/test/utils.js index 21f69d94126..9b1610583e5 100644 --- a/test/utils.js +++ b/test/utils.js @@ -12,6 +12,7 @@ exports.loader = loader; exports.normaliseOutput = normaliseOutput; exports.runTestSuiteWithSamples = runTestSuiteWithSamples; exports.assertDirectoriesAreEqual = assertDirectoriesAreEqual; +exports.assertFilesAreEqual = assertFilesAreEqual; exports.assertIncludes = assertIncludes; function normaliseError(error) { From 26ced57f55dbdb48c8a369e49bff1318749ae51c Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Thu, 28 Jan 2021 06:22:30 +0100 Subject: [PATCH 3/5] Update test suites --- .eslintrc.json | 81 +++++++++--------- .nycrc | 2 +- .vscode/launch.json | 3 - .vscode/settings.json | 1 - browser/path.ts | 10 ++- package.json | 31 ++++--- rollup.config.js | 2 +- src/utils/relativeId.ts | 6 +- test/browser/index.js | 76 +++++++++++++++++ test/browser/index.mjs | 83 ------------------- test/browser/samples/basic/_config.js | 1 - .../renormalizes-external-paths/_config.js | 42 ++++++++++ .../_expected/entry.js | 2 + .../_expected/main.js | 2 + .../samples/supports-hashes/_config.js | 16 ++++ .../supports-hashes/_expected/dep-cf8755fa.js | 3 + .../_expected/main-53604712.js | 3 + 17 files changed, 209 insertions(+), 155 deletions(-) create mode 100644 test/browser/index.js delete mode 100644 test/browser/index.mjs create mode 100644 test/browser/samples/renormalizes-external-paths/_config.js create mode 100644 test/browser/samples/renormalizes-external-paths/_expected/entry.js create mode 100644 test/browser/samples/renormalizes-external-paths/_expected/main.js create mode 100644 test/browser/samples/supports-hashes/_config.js create mode 100644 test/browser/samples/supports-hashes/_expected/dep-cf8755fa.js create mode 100644 test/browser/samples/supports-hashes/_expected/main-53604712.js diff --git a/.eslintrc.json b/.eslintrc.json index 06e4f25029e..63fa0daa3aa 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,46 +6,43 @@ "**/*.ts" ], "rules": { - "indent": [2, "tab", { "SwitchCase": 1 }], - "semi": [2, "always"], - "keyword-spacing": [2, { "before": true, "after": true }], - "space-before-blocks": [2, "always"], - "no-mixed-spaces-and-tabs": [2, "smart-tabs"], - "no-cond-assign": 0, - "no-unused-vars": 2, - "object-shorthand": [2, "always"], - "no-const-assign": 2, - "no-class-assign": 2, - "no-this-before-super": 2, - "no-var": 2, - "no-unreachable": 2, - "valid-typeof": 2, - "quote-props": [2, "as-needed"], - "one-var": [2, "never"], - "prefer-arrow-callback": 2, - "prefer-const": [2, { "destructuring": "all" }], - "arrow-spacing": 2 - }, - "env": { - "es6": true, - "browser": true, - "node": true - }, - "extends": ["eslint:recommended", "plugin:import/errors", "plugin:import/warnings"], - "parserOptions": { - "ecmaVersion": 2018, - "sourceType": "module" - }, - "settings": { - "import/ignore": [0, ["\\.path.js$"]] - }, - "overrides": [ - { - "files": ["./test/**"], - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module" - } - } - ] + "indent": [ 2, "tab", { "SwitchCase": 1 } ], + "semi": [ 2, "always" ], + "keyword-spacing": [ 2, { "before": true, "after": true } ], + "space-before-blocks": [ 2, "always" ], + "no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ], + "no-cond-assign": 0, + "no-unused-vars": 2, + "object-shorthand": [ 2, "always" ], + "no-const-assign": 2, + "no-class-assign": 2, + "no-this-before-super": 2, + "no-var": 2, + "no-unreachable": 2, + "valid-typeof": 2, + "quote-props": [ 2, "as-needed" ], + "one-var": [ 2, "never" ], + "prefer-arrow-callback": 2, + "prefer-const": [ 2, { "destructuring": "all" } ], + "arrow-spacing": 2 + }, + "env": { + "es6": true, + "browser": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:import/errors", + "plugin:import/warnings" + ], + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "settings": { + "import/ignore": [ 0, [ + "\\.path.js$" + ] ] + } } diff --git a/.nycrc b/.nycrc index 73750dd99c3..af7db356dc9 100644 --- a/.nycrc +++ b/.nycrc @@ -1,4 +1,4 @@ { - "exclude": ["*commonjsHelpers.js", "test"], + "exclude": ["test"], "extension": [".ts", ""] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 6f554c9d2dd..304068cdc4c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,7 +1,4 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { diff --git a/.vscode/settings.json b/.vscode/settings.json index 0208e5b23c5..b850ff46a88 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,3 @@ -// Place your settings in this file to overwrite default and user settings. { "typescript.format.insertSpaceBeforeFunctionParenthesis": true, "typescript.format.insertSpaceAfterConstructor": true, diff --git a/browser/path.ts b/browser/path.ts index ecae1fa030f..83884c209c9 100644 --- a/browser/path.ts +++ b/browser/path.ts @@ -58,9 +58,13 @@ export function relative(from: string, to: string) { } export function resolve(...paths: string[]) { - let resolvedParts = paths.shift()!.split(/[/\\]/); + const firstPathSegment = paths.shift(); + if (!firstPathSegment) { + return '/'; + } + let resolvedParts = firstPathSegment.split(/[/\\]/); - paths.forEach(path => { + for (const path of paths) { if (isAbsolute(path)) { resolvedParts = path.split(/[/\\]/); } else { @@ -75,7 +79,7 @@ export function resolve(...paths: string[]) { resolvedParts.push.apply(resolvedParts, parts); } - }); + } return resolvedParts.join('/'); } diff --git a/package.json b/package.json index ed6c84f21d7..aacdae13864 100644 --- a/package.json +++ b/package.json @@ -10,37 +10,34 @@ }, "scripts": { "build": "shx rm -rf dist && git rev-parse HEAD > .commithash && rollup -c && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup", - "build:test": "shx rm -rf dist && rollup -c --configTest && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup", + "build:cjs": "shx rm -rf dist && rollup -c --configTest && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup", "build:bootstrap": "dist/bin/rollup -c && shx cp src/rollup/types.d.ts dist/rollup.d.ts && shx chmod a+x dist/bin/rollup", "ci:lint": "npm run lint:nofix", - "ci:test": "npm run build:test && npm run build:bootstrap && npm run test:all", - "ci:test:only": "npm run build:test && npm run build:bootstrap && npm run test:only", - "ci:coverage": "npm run build:test && nyc --reporter lcovonly mocha && codecov", + "ci:test": "npm run build:cjs && npm run build:bootstrap && npm run test:all", + "ci:test:only": "npm run build:cjs && npm run build:bootstrap && npm run test:only", + "ci:coverage": "npm run build:cjs && nyc --reporter lcovonly mocha && codecov", "lint": "npm run lint:ts -- --fix && npm run lint:js -- --fix && npm run lint:markdown", "lint:nofix": "npm run lint:ts && npm run lint:js && npm run lint:markdown", "lint:ts": "tslint --project .", "lint:js": "eslint test/test.js test/*/index.js test/utils.js test/**/_config.js", "lint:markdown": "markdownlint --config markdownlint.json docs/**/*.md", - "perf": "npm run build:test && node --expose-gc scripts/perf.js", + "perf": "npm run build:cjs && node --expose-gc scripts/perf.js", "perf:debug": "node --inspect-brk scripts/perf-debug.js", "perf:init": "node scripts/perf-init.js", "prepare": "npm run build", "prepublishOnly": "npm ci && npm run lint:nofix && npm run security && npm run build:bootstrap && npm run test:all", - "pretest": "npm run build:test", - "pretest:browser": "npm run build", - "pretest:coverage": "npm run build:test && shx rm -rf coverage/*", - "pretest:typescript": "shx rm -rf test/typescript/dist && shx cp -r dist test/typescript/", - "security": "# npm audit # deactivated until there is a solution for the lodash issue", - "test": "npm run test:all", - "test:all": "npm run test:only && npm run test:typescript && npm run test:leak && npm run test:package", - "test:coverage": "nyc --reporter html mocha test/test.js", + "security": "npm audit", + "test": "npm run build && npm run test:all", + "test:cjs": "npm run build:cjs && npm run test:only", + "test:quick": "mocha -b test/test.js", + "test:all": "npm run test:only && npm run test:browser && npm run test:typescript && npm run test:leak && npm run test:package", + "test:coverage": "npm run build:cjs && shx rm -rf coverage/* && nyc --reporter html mocha test/test.js", + "test:coverage:browser": "npm run build && shx rm -rf coverage/* && nyc mocha test/browser/index.js", "test:leak": "node --expose-gc test/leak/index.js", "test:package": "node scripts/test-package.js", "test:only": "mocha test/test.js", - "test:quick": "mocha -b test/test.js", - "test:typescript": "tsc --noEmit -p test/typescript && tsc --noEmit", - "test:browser": "mocha test/browser/index.mjs", - "test:browser:quick": "mocha -b test/browser/index.mjs", + "test:typescript": "shx rm -rf test/typescript/dist && shx cp -r dist test/typescript/ && tsc --noEmit -p test/typescript && tsc --noEmit", + "test:browser": "mocha test/browser/index.js", "watch": "rollup -cw" }, "repository": "rollup/rollup", diff --git a/rollup.config.js b/rollup.config.js index eb18ad82c6f..4dddad1bb99 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -156,7 +156,7 @@ export default command => { treeshake, strictDeprecations: true, output: [ - { file: 'dist/rollup.browser.js', format: 'umd', name: 'rollup', banner }, + { file: 'dist/rollup.browser.js', format: 'umd', name: 'rollup', banner, sourcemap: true }, { file: 'dist/es/rollup.browser.js', format: 'es', banner } ] }; diff --git a/src/utils/relativeId.ts b/src/utils/relativeId.ts index cd98de928c7..e3d3d36475b 100644 --- a/src/utils/relativeId.ts +++ b/src/utils/relativeId.ts @@ -1,4 +1,4 @@ -import { basename, extname, isAbsolute, relative } from './path'; +import { basename, extname, isAbsolute, relative, resolve } from './path'; import { sanitizeFileName } from './sanitizeFileName'; export function getAliasName(id: string) { @@ -7,8 +7,8 @@ export function getAliasName(id: string) { } export default function relativeId(id: string) { - if (typeof process === 'undefined' || !isAbsolute(id)) return id; - return relative(process.cwd(), id); + if (!isAbsolute(id)) return id; + return relative(resolve(), id); } export function isPlainPathFragment(name: string) { diff --git a/test/browser/index.js b/test/browser/index.js new file mode 100644 index 00000000000..d77fcc5ac90 --- /dev/null +++ b/test/browser/index.js @@ -0,0 +1,76 @@ +const fixturify = require('fixturify'); +const { basename, resolve } = require('path'); +const { rollup } = require('../../dist/rollup.browser.js'); +const { assertFilesAreEqual, runTestSuiteWithSamples, compareError } = require('../utils.js'); + +runTestSuiteWithSamples('browser', resolve(__dirname, 'samples'), (dir, config) => { + (config.skip ? it.skip : config.solo ? it.only : it)( + basename(dir) + ': ' + config.description, + async () => { + let bundle; + try { + bundle = await rollup({ + input: 'main', + onwarn: warning => { + if (!(config.expectedWarnings && config.expectedWarnings.indexOf(warning.code) >= 0)) { + throw new Error( + `Unexpected warnings (${warning.code}): ${warning.message}\n` + + 'If you expect warnings, list their codes in config.expectedWarnings' + ); + } + }, + strictDeprecations: true, + ...config.options + }); + } catch (error) { + if (config.error) { + compareError(error, config.error); + return; + } else { + throw error; + } + } + if (config.error) { + throw new Error('Expected an error while rolling up'); + } + let output; + try { + ({ output } = await bundle.generate({ + exports: 'auto', + format: 'es', + ...(config.options || {}).output + })); + } catch (error) { + if (config.generateError) { + compareError(error, config.generateError); + return; + } else { + throw error; + } + } + if (config.generateError) { + throw new Error('Expected an error while generating output'); + } + assertOutputMatches(output, dir); + } + ); +}); + +function assertOutputMatches(output, dir) { + const actual = {}; + for (const file of output) { + const filePath = file.fileName.split('/'); + const fileName = filePath.pop(); + let currentDir = actual; + for (const pathElement of filePath) { + if (!currentDir[pathElement]) { + currentDir[pathElement] = {}; + } + currentDir = currentDir[pathElement] = currentDir[pathElement] || {}; + } + currentDir[fileName] = file.source || file.code; + } + fixturify.writeSync(resolve(dir, '_actual'), actual); + const expected = fixturify.readSync(resolve(dir, '_expected')); + assertFilesAreEqual(actual, expected); +} diff --git a/test/browser/index.mjs b/test/browser/index.mjs deleted file mode 100644 index 84bd5cb93eb..00000000000 --- a/test/browser/index.mjs +++ /dev/null @@ -1,83 +0,0 @@ -import fixturify from 'fixturify'; -import { basename, dirname, resolve } from 'path'; -import { fileURLToPath } from 'url'; -import { rollup } from '../../dist/es/rollup.browser.js'; -import { assertFilesAreEqual, runTestSuiteWithSamples, compareError } from '../utils.js'; - -runTestSuiteWithSamples( - 'form', - resolve(dirname(fileURLToPath(import.meta.url)), 'samples'), - (dir, config) => { - (config.skip ? it.skip : config.solo ? it.only : it)( - basename(dir) + ': ' + config.description, - async () => { - let bundle; - try { - bundle = await rollup({ - input: 'main', - onwarn: warning => { - if ( - !(config.expectedWarnings && config.expectedWarnings.indexOf(warning.code) >= 0) - ) { - throw new Error( - `Unexpected warnings (${warning.code}): ${warning.message}\n` + - 'If you expect warnings, list their codes in config.expectedWarnings' - ); - } - }, - strictDeprecations: true, - ...config.options - }); - } catch (error) { - if (config.error) { - compareError(error, config.error); - return; - } else { - throw error; - } - } - if (config.error) { - throw new Error('Expected an error while rolling up'); - } - let output; - try { - ({ output } = await bundle.generate({ - exports: 'auto', - format: 'es', - ...(config.options || {}).output - })); - } catch (error) { - if (config.generateError) { - compareError(error, config.generateError); - return; - } else { - throw error; - } - } - if (config.generateError) { - throw new Error('Expected an error while generating output'); - } - assertOutputMatches(output, dir); - } - ); - } -); - -function assertOutputMatches(output, dir) { - const actual = {}; - for (const file of output) { - const filePath = file.fileName.split('/'); - const fileName = filePath.pop(); - let currentDir = actual; - for (const pathElement of filePath) { - if (!currentDir[pathElement]) { - currentDir[pathElement] = {}; - } - currentDir = currentDir[pathElement] = currentDir[pathElement] || {}; - } - currentDir[fileName] = file.source || file.code; - } - fixturify.writeSync(resolve(dir, '_actual'), actual); - const expected = fixturify.readSync(resolve(dir, '_expected')); - assertFilesAreEqual(actual, expected); -} diff --git a/test/browser/samples/basic/_config.js b/test/browser/samples/basic/_config.js index 91c5821a80a..a1881638888 100644 --- a/test/browser/samples/basic/_config.js +++ b/test/browser/samples/basic/_config.js @@ -1,7 +1,6 @@ const { loader } = require('../../../utils.js'); module.exports = { - show: true, description: 'bundles files for the browser', options: { plugins: loader({ diff --git a/test/browser/samples/renormalizes-external-paths/_config.js b/test/browser/samples/renormalizes-external-paths/_config.js new file mode 100644 index 00000000000..5d37b6e129f --- /dev/null +++ b/test/browser/samples/renormalizes-external-paths/_config.js @@ -0,0 +1,42 @@ +const { join, dirname } = require('path'); + +module.exports = { + description: 'renormalizes external paths if possible', + options: { + input: ['/main.js', '/nested/entry.js'], + external(id) { + return id.endsWith('ext'); + }, + plugins: { + resolveId(source, importer) { + if (source.endsWith('ext.js')) { + return false; + } + if (!importer) { + return source; + } + return join(dirname(importer), source); + }, + load(id) { + switch (id) { + case '/main.js': + return `import './nested/dep.js'; +import './ext.js'; +import './nested/nested-ext.js';`; + case '/dep.js': + return `import './ext.js'; +import './nested/nested-ext.js';`; + case '/nested/dep.js': + return `import '../ext.js'; +import './nested-ext.js';`; + case '/nested/entry.js': + return `import '../dep.js'; +import '../ext.js'; +import './nested-ext.js';`; + default: + throw new Error(`Unexpected id ${id}`); + } + } + } + } +}; diff --git a/test/browser/samples/renormalizes-external-paths/_expected/entry.js b/test/browser/samples/renormalizes-external-paths/_expected/entry.js new file mode 100644 index 00000000000..60a5c58c477 --- /dev/null +++ b/test/browser/samples/renormalizes-external-paths/_expected/entry.js @@ -0,0 +1,2 @@ +import './ext.js'; +import './nested/nested-ext.js'; diff --git a/test/browser/samples/renormalizes-external-paths/_expected/main.js b/test/browser/samples/renormalizes-external-paths/_expected/main.js new file mode 100644 index 00000000000..60a5c58c477 --- /dev/null +++ b/test/browser/samples/renormalizes-external-paths/_expected/main.js @@ -0,0 +1,2 @@ +import './ext.js'; +import './nested/nested-ext.js'; diff --git a/test/browser/samples/supports-hashes/_config.js b/test/browser/samples/supports-hashes/_config.js new file mode 100644 index 00000000000..6079c64980d --- /dev/null +++ b/test/browser/samples/supports-hashes/_config.js @@ -0,0 +1,16 @@ +const { loader } = require('../../../utils.js'); + +module.exports = { + description: 'bundles files for the browser', + options: { + input: ['main', 'dep'], + plugins: loader({ + main: `import {foo} from 'dep'; +console.log(foo);`, + dep: `export const foo = 42;` + }), + output: { + entryFileNames: '[name]-[hash].js' + } + } +}; diff --git a/test/browser/samples/supports-hashes/_expected/dep-cf8755fa.js b/test/browser/samples/supports-hashes/_expected/dep-cf8755fa.js new file mode 100644 index 00000000000..b72a22eac84 --- /dev/null +++ b/test/browser/samples/supports-hashes/_expected/dep-cf8755fa.js @@ -0,0 +1,3 @@ +const foo = 42; + +export { foo }; diff --git a/test/browser/samples/supports-hashes/_expected/main-53604712.js b/test/browser/samples/supports-hashes/_expected/main-53604712.js new file mode 100644 index 00000000000..347f7a59847 --- /dev/null +++ b/test/browser/samples/supports-hashes/_expected/main-53604712.js @@ -0,0 +1,3 @@ +import { foo } from './dep-cf8755fa.js'; + +console.log(foo); From 4770ed50a56e1f35c4d67be8aeb3e2e25e0f0ced Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Thu, 28 Jan 2021 10:03:45 +0100 Subject: [PATCH 4/5] Update browser/error.ts Co-authored-by: Craig Morten --- browser/error.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/browser/error.ts b/browser/error.ts index 951e87cf362..31cf10ea52a 100644 --- a/browser/error.ts +++ b/browser/error.ts @@ -1,8 +1,9 @@ +import { error } from '../src/utils/error'; + export const throwNoFileSystem = (method: string) => (..._args: any[]): never => { - throw Object.assign( - new Error( - `Cannot access the file system (via "${method}") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.` - ), - { code: 'NO_FS_IN_BROWSER', url: 'https://rollupjs.org/guide/en/#a-simple-example' } - ); + error({ + code: 'NO_FS_IN_BROWSER', + message: `Cannot access the file system (via "${method}") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.`, + url: 'https://rollupjs.org/guide/en/#a-simple-example' + }); }; From 37bdbee3382984c2c3dc9c556f90581604c016d2 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Thu, 28 Jan 2021 10:57:27 +0100 Subject: [PATCH 5/5] Use posix paths in test config --- test/browser/samples/renormalizes-external-paths/_config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/browser/samples/renormalizes-external-paths/_config.js b/test/browser/samples/renormalizes-external-paths/_config.js index 5d37b6e129f..fae340e6bc8 100644 --- a/test/browser/samples/renormalizes-external-paths/_config.js +++ b/test/browser/samples/renormalizes-external-paths/_config.js @@ -1,4 +1,4 @@ -const { join, dirname } = require('path'); +const { join, dirname } = require('path').posix; module.exports = { description: 'renormalizes external paths if possible',