diff --git a/.changeset/tricky-islands-approve.md b/.changeset/tricky-islands-approve.md new file mode 100644 index 00000000..cba1fb26 --- /dev/null +++ b/.changeset/tricky-islands-approve.md @@ -0,0 +1,5 @@ +--- +'pleasantest': minor +--- + +Add suggestion to error message when transformation plugin is missing for unrecognized file extensions diff --git a/package-lock.json b/package-lock.json index 8ba0f6cb..e2a44bdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,11 +61,14 @@ "remark-validate-links": "11.0.2", "resolve.exports": "1.1.0", "rollup-plugin-dts": "3.0.2", + "rollup-plugin-svelte": "^7.1.0", "rollup-plugin-terser": "7.0.2", "rollup-plugin-vue": "6.0.0", "sass": "1.44.0", "simple-code-frame": "1.2.0", "smoldash": "0.11.0", + "svelte": "^3.44.2", + "svelte-preprocess": "^4.9.8", "typescript": "4.5.2", "vue": "3.2.23" }, @@ -4432,6 +4435,12 @@ "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==", "dev": true }, + "node_modules/@types/pug": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.5.tgz", + "integrity": "sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA==", + "dev": true + }, "node_modules/@types/puppeteer": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.4.tgz", @@ -4468,6 +4477,15 @@ "@types/node": "*" } }, + "node_modules/@types/sass": { + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.43.1.tgz", + "integrity": "sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.2.tgz", @@ -7229,6 +7247,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", + "dev": true + }, "node_modules/esbuild": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.2.tgz", @@ -15033,6 +15057,18 @@ "node": ">= 8.0.0" } }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -17500,6 +17536,12 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "node_modules/require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, "node_modules/reserved-words": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", @@ -17743,6 +17785,23 @@ "node": ">=8" } }, + "node_modules/rollup-plugin-svelte": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz", + "integrity": "sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==", + "dev": true, + "dependencies": { + "require-relative": "^0.8.7", + "rollup-pluginutils": "^2.8.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "rollup": ">=2.0.0", + "svelte": ">=3.5.0" + } + }, "node_modules/rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", @@ -17843,6 +17902,30 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/sander": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", + "integrity": "sha1-dB4kXiMfB8r7b98PEzrfohalAq0=", + "dev": true, + "dependencies": { + "es6-promise": "^3.1.2", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2" + } + }, + "node_modules/sander/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/sane": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", @@ -18590,6 +18673,21 @@ "urix": "^0.1.0" } }, + "node_modules/sorcery": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", + "integrity": "sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=", + "dev": true, + "dependencies": { + "buffer-crc32": "^0.2.5", + "minimist": "^1.2.0", + "sander": "^0.5.0", + "sourcemap-codec": "^1.3.0" + }, + "bin": { + "sorcery": "bin/index.js" + } + }, "node_modules/source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", @@ -19172,6 +19270,81 @@ "node": ">=8" } }, + "node_modules/svelte": { + "version": "3.44.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.44.2.tgz", + "integrity": "sha512-jrZhZtmH3ZMweXg1Q15onb8QlWD+a5T5Oca4C1jYvSURp2oD35h4A5TV6t6MEa93K4LlX6BkafZPdQoFjw/ylA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/svelte-preprocess": { + "version": "4.9.8", + "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.9.8.tgz", + "integrity": "sha512-EQS/oRZzMtYdAprppZxY3HcysKh11w54MgA63ybtL+TAZ4hVqYOnhw41JVJjWN9dhPnNjjLzvbZ2tMhTsla1Og==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@types/pug": "^2.0.4", + "@types/sass": "^1.16.0", + "detect-indent": "^6.0.0", + "magic-string": "^0.25.7", + "sorcery": "^0.10.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">= 9.11.2" + }, + "peerDependencies": { + "@babel/core": "^7.10.2", + "coffeescript": "^2.5.1", + "less": "^3.11.3", + "postcss": "^7 || ^8", + "postcss-load-config": "^2.1.0 || ^3.0.0", + "pug": "^3.0.0", + "sass": "^1.26.8", + "stylus": "^0.54.7", + "sugarss": "^2.0.0", + "svelte": "^3.23.0", + "typescript": "^3.9.5 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "coffeescript": { + "optional": true + }, + "less": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "postcss": { + "optional": true + }, + "postcss-load-config": { + "optional": true + }, + "pug": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/svgo": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.4.0.tgz", @@ -24238,6 +24411,12 @@ "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==", "dev": true }, + "@types/pug": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.5.tgz", + "integrity": "sha512-LOnASQoeNZMkzexRuyqcBBDZ6rS+rQxUMkmj5A0PkhhiSZivLIuz6Hxyr1mkGoEZEkk66faROmpMi4fFkrKsBA==", + "dev": true + }, "@types/puppeteer": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-5.4.4.tgz", @@ -24274,6 +24453,15 @@ "@types/node": "*" } }, + "@types/sass": { + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/@types/sass/-/sass-1.43.1.tgz", + "integrity": "sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/semver": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.2.tgz", @@ -26406,6 +26594,12 @@ "is-symbol": "^1.0.2" } }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", + "dev": true + }, "esbuild": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.2.tgz", @@ -32225,6 +32419,15 @@ "integrity": "sha512-NaeZIckeBFT7i0XBEpGyFcAE0/bLcQ9MHErTpnU3bLWVE5WZbxG5Y3fDsMxYGifTo5khDA42OquXCC2ngKJB+g==", "dev": true }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, "mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -34016,6 +34219,12 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, "reserved-words": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", @@ -34184,6 +34393,16 @@ } } }, + "rollup-plugin-svelte": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz", + "integrity": "sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==", + "dev": true, + "requires": { + "require-relative": "^0.8.7", + "rollup-pluginutils": "^2.8.2" + } + }, "rollup-plugin-terser": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", @@ -34263,6 +34482,29 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sander": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", + "integrity": "sha1-dB4kXiMfB8r7b98PEzrfohalAq0=", + "dev": true, + "requires": { + "es6-promise": "^3.1.2", + "graceful-fs": "^4.1.3", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "sane": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", @@ -34871,6 +35113,18 @@ } } }, + "sorcery": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.10.0.tgz", + "integrity": "sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=", + "dev": true, + "requires": { + "buffer-crc32": "^0.2.5", + "minimist": "^1.2.0", + "sander": "^0.5.0", + "sourcemap-codec": "^1.3.0" + } + }, "source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", @@ -35350,6 +35604,26 @@ } } }, + "svelte": { + "version": "3.44.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.44.2.tgz", + "integrity": "sha512-jrZhZtmH3ZMweXg1Q15onb8QlWD+a5T5Oca4C1jYvSURp2oD35h4A5TV6t6MEa93K4LlX6BkafZPdQoFjw/ylA==", + "dev": true + }, + "svelte-preprocess": { + "version": "4.9.8", + "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-4.9.8.tgz", + "integrity": "sha512-EQS/oRZzMtYdAprppZxY3HcysKh11w54MgA63ybtL+TAZ4hVqYOnhw41JVJjWN9dhPnNjjLzvbZ2tMhTsla1Og==", + "dev": true, + "requires": { + "@types/pug": "^2.0.4", + "@types/sass": "^1.16.0", + "detect-indent": "^6.0.0", + "magic-string": "^0.25.7", + "sorcery": "^0.10.0", + "strip-indent": "^3.0.0" + } + }, "svgo": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.4.0.tgz", diff --git a/package.json b/package.json index 9884bd55..244f244e 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,14 @@ "remark-validate-links": "11.0.2", "resolve.exports": "1.1.0", "rollup-plugin-dts": "3.0.2", + "rollup-plugin-svelte": "^7.1.0", "rollup-plugin-terser": "7.0.2", "rollup-plugin-vue": "6.0.0", "sass": "1.44.0", "simple-code-frame": "1.2.0", "smoldash": "0.11.0", + "svelte": "^3.44.2", + "svelte-preprocess": "^4.9.8", "typescript": "4.5.2", "vue": "3.2.23" }, diff --git a/src/module-server/transform-imports.ts b/src/module-server/transform-imports.ts index 0012cbc2..cfa13fc3 100644 --- a/src/module-server/transform-imports.ts +++ b/src/module-server/transform-imports.ts @@ -36,6 +36,8 @@ import type { DecodedSourceMap, RawSourceMap, } from '@ampproject/remapping/dist/types/types'; +import { extname } from 'path'; +import { jsExts } from './extensions-and-detection'; type MaybePromise = Promise | T; type ResolveFn = ( @@ -67,8 +69,12 @@ export const transformImports = async ( const linesUntilError = code.slice(0, error.idx).split('\n'); const line = linesUntilError.length; const column = linesUntilError[linesUntilError.length - 1].length; + const ext = extname(id); + const suggestion = jsExts.test(ext) + ? '' + : ` Did you mean to add a transform plugin to support ${ext} files?`; const modifiedError = new ErrorWithLocation({ - message: `Error parsing module with es-module-lexer`, + message: `Error parsing module with es-module-lexer.${suggestion}`, line, column, filename: id, diff --git a/tests/utils/runJS.test.tsx b/tests/utils/runJS.test.tsx index b5ae1bad..c9ea915f 100644 --- a/tests/utils/runJS.test.tsx +++ b/tests/utils/runJS.test.tsx @@ -2,6 +2,8 @@ import { withBrowser } from 'pleasantest'; import type { PleasantestContext, PleasantestUtils } from 'pleasantest'; import { formatErrorWithCodeFrame, printErrorFrames } from '../test-utils'; import vuePlugin from 'rollup-plugin-vue'; +import sveltePlugin from 'rollup-plugin-svelte'; +import sveltePreprocess from 'svelte-preprocess'; import aliasPlugin from '@rollup/plugin-alias'; import babel from '@rollup/plugin-babel'; @@ -198,7 +200,7 @@ test( await expect(formatErrorWithCodeFrame(runPromise)).rejects .toThrowErrorMatchingInlineSnapshot(` - "Error parsing module with es-module-lexer + "Error parsing module with es-module-lexer. /tests/utils/runJS.test.tsx:###:### @@ -434,6 +436,61 @@ describe('Ecosystem interoperability', () => { }, ), ); + test( + 'svelte component can be imported via rollup-plugin-svelte', + withBrowser( + { + moduleServer: { + plugins: [sveltePlugin({ preprocess: sveltePreprocess() })], + }, + }, + async ({ utils, screen, user }) => { + await utils.injectHTML('
'); + await utils.runJS(` + import CounterComponent from './svelte-component.svelte' + + new CounterComponent({ + target: document.getElementById('app') + }) + `); + const button = await screen.getByRole('button'); + await expect(button).toHaveTextContent(/^clicked 0 times$/i); + + await user.click(button); + await expect(button).toHaveTextContent(/^clicked 1 time$/i); + + // Check that the scss in the svelte component file got preprocessed and applied correctly + const bgColor = await button.evaluate( + (button) => getComputedStyle(button).backgroundColor, + ); + expect(bgColor).toEqual('rgb(255, 0, 0)'); + }, + ), + ); + test( + 'useful error message is thrown when plugin is missing for imported file with unusual extension', + withBrowser(async ({ utils }) => { + const runPromise = utils.runJS(` + import CounterComponent from './svelte-component.svelte' + `); + + await expect(formatErrorWithCodeFrame(runPromise)).rejects + .toThrowErrorMatchingInlineSnapshot(` + "Error parsing module with es-module-lexer. Did you mean to add a transform plugin to support .svelte files? + + /tests/utils/svelte-component.svelte:###:### + + # | count += 1; + # | } + > # | + | ^ + # | + ## | + +