From dcfe85ecdbe39f71138469a5faceb1fadf8b0c8f Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 13 Jan 2019 18:44:10 -0500 Subject: [PATCH 1/3] improve preprocess tests --- test/preprocess/index.js | 231 ++---------------- test/preprocess/samples/filename/_config.js | 20 ++ test/preprocess/samples/filename/input.html | 3 + test/preprocess/samples/filename/output.html | 3 + .../samples/ignores-null/_config.js | 5 + .../samples/ignores-null/input.html | 3 + .../samples/ignores-null/output.html | 3 + test/preprocess/samples/markup/_config.js | 9 + test/preprocess/samples/markup/input.html | 1 + test/preprocess/samples/markup/output.html | 1 + .../samples/script-multiple/_config.js | 9 + .../samples/script-multiple/input.html | 9 + .../samples/script-multiple/output.html | 9 + test/preprocess/samples/script/_config.js | 9 + test/preprocess/samples/script/input.html | 3 + test/preprocess/samples/script/output.html | 3 + .../preprocess/samples/style-async/_config.js | 9 + .../preprocess/samples/style-async/input.html | 7 + .../samples/style-async/output.html | 7 + .../samples/style-attributes/_config.js | 14 ++ .../samples/style-attributes/input.html | 1 + .../samples/style-attributes/output.html | 1 + test/preprocess/samples/style/_config.js | 9 + test/preprocess/samples/style/input.html | 7 + test/preprocess/samples/style/output.html | 7 + 25 files changed, 167 insertions(+), 216 deletions(-) create mode 100644 test/preprocess/samples/filename/_config.js create mode 100644 test/preprocess/samples/filename/input.html create mode 100644 test/preprocess/samples/filename/output.html create mode 100644 test/preprocess/samples/ignores-null/_config.js create mode 100644 test/preprocess/samples/ignores-null/input.html create mode 100644 test/preprocess/samples/ignores-null/output.html create mode 100644 test/preprocess/samples/markup/_config.js create mode 100644 test/preprocess/samples/markup/input.html create mode 100644 test/preprocess/samples/markup/output.html create mode 100644 test/preprocess/samples/script-multiple/_config.js create mode 100644 test/preprocess/samples/script-multiple/input.html create mode 100644 test/preprocess/samples/script-multiple/output.html create mode 100644 test/preprocess/samples/script/_config.js create mode 100644 test/preprocess/samples/script/input.html create mode 100644 test/preprocess/samples/script/output.html create mode 100644 test/preprocess/samples/style-async/_config.js create mode 100644 test/preprocess/samples/style-async/input.html create mode 100644 test/preprocess/samples/style-async/output.html create mode 100644 test/preprocess/samples/style-attributes/_config.js create mode 100644 test/preprocess/samples/style-attributes/input.html create mode 100644 test/preprocess/samples/style-attributes/output.html create mode 100644 test/preprocess/samples/style/_config.js create mode 100644 test/preprocess/samples/style/input.html create mode 100644 test/preprocess/samples/style/output.html diff --git a/test/preprocess/index.js b/test/preprocess/index.js index 10661efd39..9d1e8b01c0 100644 --- a/test/preprocess/index.js +++ b/test/preprocess/index.js @@ -1,226 +1,25 @@ +import * as fs from 'fs'; import * as assert from 'assert'; -import { svelte } from '../helpers.js'; +import { loadConfig, svelte } from '../helpers.js'; -describe('preprocess', () => { - it('preprocesses entire component', () => { - const source = ` -

Hello __NAME__!

- `; +describe.only('preprocess', () => { + fs.readdirSync('test/preprocess/samples').forEach(dir => { + if (dir[0] === '.') return; - const expected = ` -

Hello world!

- `; + const config = loadConfig(`./preprocess/samples/${dir}/_config.js`); - return svelte.preprocess(source, { - markup: ({ content }) => { - return { - code: content.replace('__NAME__', 'world') - }; - } - }).then(processed => { - assert.equal(processed.toString(), expected); - }); - }); - - it('preprocesses style', () => { - const source = ` -
$brand
- - - `; - - const expected = ` -
$brand
- - - `; - - return svelte.preprocess(source, { - style: ({ content }) => { - return { - code: content.replace('$brand', 'purple') - }; - } - }).then(processed => { - assert.equal(processed.toString(), expected); - }); - }); - - it('preprocesses style asynchronously', () => { - const source = ` -
$brand
- - - `; - - const expected = ` -
$brand
- - - `; - - return svelte.preprocess(source, { - style: ({ content }) => { - return Promise.resolve({ - code: content.replace('$brand', 'purple') - }); - } - }).then(processed => { - assert.equal(processed.toString(), expected); - }); - }); - - it('preprocesses script', () => { - const source = ` - - `; - - const expected = ` - - `; - - return svelte.preprocess(source, { - script: ({ content }) => { - return { - code: content.replace('__THE_ANSWER__', '42') - }; - } - }).then(processed => { - assert.equal(processed.toString(), expected); - }); - }); - - it('preprocesses multiple matching tags', () => { - const source = ` - - - - `; - - const expected = ` - - - - `; - - return svelte.preprocess(source, { - script: ({ content }) => { - return { - code: content.toLowerCase() - }; - } - }).then(processed => { - assert.equal(processed.toString(), expected); - }); - }); - - it('parses attributes', () => { - const source = ` - - `; - - const expected = ` - - `; - - return svelte.preprocess(source, { - style: ({ attributes }) => { - assert.deepEqual(attributes, { - type: 'text/scss', - 'data-foo': 'bar', - bool: true - }); - return { code: 'PROCESSED' }; - } - }).then(processed => { - assert.equal(processed.toString(), expected); - }); - }); - - it('provides filename to processing hooks', () => { - const source = ` -

Hello __MARKUP_FILENAME__!

- - - `; - - const expected = ` -

Hello file.html!

- - - `; - - return svelte.preprocess(source, { - filename: 'file.html', - markup: ({ content, filename }) => { - return { - code: content.replace('__MARKUP_FILENAME__', filename) - }; - }, - style: ({ content, filename }) => { - return { - code: content.replace('__STYLE_FILENAME__', filename) - }; - }, - script: ({ content, filename }) => { - return { - code: content.replace('__SCRIPT_FILENAME__', filename) - }; - } - }).then(processed => { - assert.equal(processed.toString(), expected); - }); - }); + if (config.solo && process.env.CI) { + throw new Error('Forgot to remove `solo: true` from test'); + } - it('ignores null/undefined returned from preprocessor', () => { - const source = ` - - `; + (config.skip ? it.skip : config.solo ? it.only : it)(dir, async () => { + const input = fs.readFileSync(`test/preprocess/samples/${dir}/input.html`, 'utf-8'); + const expected = fs.readFileSync(`test/preprocess/samples/${dir}/output.html`, 'utf-8'); - const expected = ` - - `; + const actual = await svelte.preprocess(input, config.preprocess); + fs.writeFileSync(`test/preprocess/samples/${dir}/_actual.html`, actual); - return svelte.preprocess(source, { - script: () => null - }).then(processed => { - assert.equal(processed.toString(), expected); + assert.equal(actual, expected); }); }); }); diff --git a/test/preprocess/samples/filename/_config.js b/test/preprocess/samples/filename/_config.js new file mode 100644 index 0000000000..d7a3c19770 --- /dev/null +++ b/test/preprocess/samples/filename/_config.js @@ -0,0 +1,20 @@ +export default { + preprocess: { + filename: 'file.html', + markup: ({ content, filename }) => { + return { + code: content.replace('__MARKUP_FILENAME__', filename) + }; + }, + style: ({ content, filename }) => { + return { + code: content.replace('__STYLE_FILENAME__', filename) + }; + }, + script: ({ content, filename }) => { + return { + code: content.replace('__SCRIPT_FILENAME__', filename) + }; + } + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/filename/input.html b/test/preprocess/samples/filename/input.html new file mode 100644 index 0000000000..f03a890f6e --- /dev/null +++ b/test/preprocess/samples/filename/input.html @@ -0,0 +1,3 @@ +

Hello __MARKUP_FILENAME__!

+ + \ No newline at end of file diff --git a/test/preprocess/samples/filename/output.html b/test/preprocess/samples/filename/output.html new file mode 100644 index 0000000000..90fa180704 --- /dev/null +++ b/test/preprocess/samples/filename/output.html @@ -0,0 +1,3 @@ +

Hello file.html!

+ + \ No newline at end of file diff --git a/test/preprocess/samples/ignores-null/_config.js b/test/preprocess/samples/ignores-null/_config.js new file mode 100644 index 0000000000..042ee89aed --- /dev/null +++ b/test/preprocess/samples/ignores-null/_config.js @@ -0,0 +1,5 @@ +export default { + preprocess: { + script: () => null + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/ignores-null/input.html b/test/preprocess/samples/ignores-null/input.html new file mode 100644 index 0000000000..b3fc25b643 --- /dev/null +++ b/test/preprocess/samples/ignores-null/input.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/ignores-null/output.html b/test/preprocess/samples/ignores-null/output.html new file mode 100644 index 0000000000..b3fc25b643 --- /dev/null +++ b/test/preprocess/samples/ignores-null/output.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/markup/_config.js b/test/preprocess/samples/markup/_config.js new file mode 100644 index 0000000000..857da608af --- /dev/null +++ b/test/preprocess/samples/markup/_config.js @@ -0,0 +1,9 @@ +export default { + preprocess: { + markup: ({ content }) => { + return { + code: content.replace('__NAME__', 'world') + }; + } + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/markup/input.html b/test/preprocess/samples/markup/input.html new file mode 100644 index 0000000000..d65a1a50a6 --- /dev/null +++ b/test/preprocess/samples/markup/input.html @@ -0,0 +1 @@ +

Hello __NAME__!

\ No newline at end of file diff --git a/test/preprocess/samples/markup/output.html b/test/preprocess/samples/markup/output.html new file mode 100644 index 0000000000..efe5048cbc --- /dev/null +++ b/test/preprocess/samples/markup/output.html @@ -0,0 +1 @@ +

Hello world!

\ No newline at end of file diff --git a/test/preprocess/samples/script-multiple/_config.js b/test/preprocess/samples/script-multiple/_config.js new file mode 100644 index 0000000000..d6656ff48e --- /dev/null +++ b/test/preprocess/samples/script-multiple/_config.js @@ -0,0 +1,9 @@ +export default { + preprocess: { + script: ({ content }) => { + return { + code: content.toLowerCase() + }; + } + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/script-multiple/input.html b/test/preprocess/samples/script-multiple/input.html new file mode 100644 index 0000000000..5a5221dc84 --- /dev/null +++ b/test/preprocess/samples/script-multiple/input.html @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/test/preprocess/samples/script-multiple/output.html b/test/preprocess/samples/script-multiple/output.html new file mode 100644 index 0000000000..64fe4c1f25 --- /dev/null +++ b/test/preprocess/samples/script-multiple/output.html @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/test/preprocess/samples/script/_config.js b/test/preprocess/samples/script/_config.js new file mode 100644 index 0000000000..8e8ac1d2f9 --- /dev/null +++ b/test/preprocess/samples/script/_config.js @@ -0,0 +1,9 @@ +export default { + preprocess: { + script: ({ content }) => { + return { + code: content.replace('__THE_ANSWER__', '42') + }; + } + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/script/input.html b/test/preprocess/samples/script/input.html new file mode 100644 index 0000000000..8236f6d9d2 --- /dev/null +++ b/test/preprocess/samples/script/input.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/script/output.html b/test/preprocess/samples/script/output.html new file mode 100644 index 0000000000..5bef15aab0 --- /dev/null +++ b/test/preprocess/samples/script/output.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/style-async/_config.js b/test/preprocess/samples/style-async/_config.js new file mode 100644 index 0000000000..a91f8f4307 --- /dev/null +++ b/test/preprocess/samples/style-async/_config.js @@ -0,0 +1,9 @@ +export default { + preprocess: { + style: ({ content }) => { + return Promise.resolve({ + code: content.replace('$brand', 'purple') + }); + } + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/style-async/input.html b/test/preprocess/samples/style-async/input.html new file mode 100644 index 0000000000..303ac6366a --- /dev/null +++ b/test/preprocess/samples/style-async/input.html @@ -0,0 +1,7 @@ +
$brand
+ + \ No newline at end of file diff --git a/test/preprocess/samples/style-async/output.html b/test/preprocess/samples/style-async/output.html new file mode 100644 index 0000000000..2847cc525a --- /dev/null +++ b/test/preprocess/samples/style-async/output.html @@ -0,0 +1,7 @@ +
$brand
+ + \ No newline at end of file diff --git a/test/preprocess/samples/style-attributes/_config.js b/test/preprocess/samples/style-attributes/_config.js new file mode 100644 index 0000000000..7a619054bd --- /dev/null +++ b/test/preprocess/samples/style-attributes/_config.js @@ -0,0 +1,14 @@ +import * as assert from 'assert'; + +export default { + preprocess: { + style: ({ attributes }) => { + assert.deepEqual(attributes, { + type: 'text/scss', + 'data-foo': 'bar', + bool: true + }); + return { code: 'PROCESSED' }; + } + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/style-attributes/input.html b/test/preprocess/samples/style-attributes/input.html new file mode 100644 index 0000000000..3a55a5a3f6 --- /dev/null +++ b/test/preprocess/samples/style-attributes/input.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/style-attributes/output.html b/test/preprocess/samples/style-attributes/output.html new file mode 100644 index 0000000000..c83defeebb --- /dev/null +++ b/test/preprocess/samples/style-attributes/output.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/style/_config.js b/test/preprocess/samples/style/_config.js new file mode 100644 index 0000000000..59040eccdd --- /dev/null +++ b/test/preprocess/samples/style/_config.js @@ -0,0 +1,9 @@ +export default { + preprocess: { + style: ({ content }) => { + return { + code: content.replace('$brand', 'purple') + }; + } + } +}; \ No newline at end of file diff --git a/test/preprocess/samples/style/input.html b/test/preprocess/samples/style/input.html new file mode 100644 index 0000000000..303ac6366a --- /dev/null +++ b/test/preprocess/samples/style/input.html @@ -0,0 +1,7 @@ +
$brand
+ + \ No newline at end of file diff --git a/test/preprocess/samples/style/output.html b/test/preprocess/samples/style/output.html new file mode 100644 index 0000000000..2847cc525a --- /dev/null +++ b/test/preprocess/samples/style/output.html @@ -0,0 +1,7 @@ +
$brand
+ + \ No newline at end of file From 4936cd5ac4c020b0a3104edfbcf4445a39d8143b Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 13 Jan 2019 19:26:31 -0500 Subject: [PATCH 2/3] support multiple preprocessors, handle dependencies - fixes #1973 --- src/preprocess/index.ts | 68 +++++++++++++------ test/preprocess/index.js | 8 ++- .../samples/dependencies/_config.js | 15 ++++ .../samples/dependencies/input.html | 3 + .../samples/dependencies/output.html | 3 + .../samples/multiple-preprocessors/_config.js | 14 ++++ .../samples/multiple-preprocessors/input.html | 11 +++ .../multiple-preprocessors/output.html | 11 +++ 8 files changed, 111 insertions(+), 22 deletions(-) create mode 100644 test/preprocess/samples/dependencies/_config.js create mode 100644 test/preprocess/samples/dependencies/input.html create mode 100644 test/preprocess/samples/dependencies/output.html create mode 100644 test/preprocess/samples/multiple-preprocessors/_config.js create mode 100644 test/preprocess/samples/multiple-preprocessors/input.html create mode 100644 test/preprocess/samples/multiple-preprocessors/output.html diff --git a/src/preprocess/index.ts b/src/preprocess/index.ts index 5537b30ea1..4aaa033270 100644 --- a/src/preprocess/index.ts +++ b/src/preprocess/index.ts @@ -1,25 +1,25 @@ import { SourceMap } from 'magic-string'; import replaceAsync from '../utils/replaceAsync'; -export interface PreprocessOptions { +export interface PreprocessorGroup { markup?: (options: { content: string, filename: string - }) => { code: string, map?: SourceMap | string }; + }) => { code: string, map?: SourceMap | string, dependencies?: string[] }; style?: Preprocessor; script?: Preprocessor; - filename?: string } export type Preprocessor = (options: { content: string, attributes: Record, filename?: string -}) => { code: string, map?: SourceMap | string }; +}) => { code: string, map?: SourceMap | string, dependencies?: string[] }; interface Processed { code: string; map?: SourceMap | string; + dependencies?: string[]; } function parseAttributeValue(value: string) { @@ -39,28 +39,55 @@ function parseAttributes(str: string) { export default async function preprocess( source: string, - options: PreprocessOptions + preprocessor: PreprocessorGroup | PreprocessorGroup[], + options?: { filename?: string } ) { - if (options.markup) { - const processed: Processed = await options.markup({ + const filename = (options && options.filename) || preprocessor.filename; // legacy + const dependencies = []; + + const preprocessors = Array.isArray(preprocessor) ? preprocessor : [preprocessor]; + + const markup = preprocessors.map(p => p.markup).filter(Boolean); + const script = preprocessors.map(p => p.script).filter(Boolean); + const style = preprocessors.map(p => p.style).filter(Boolean); + + for (const fn of markup) { + const processed: Processed = await fn({ content: source, - filename: options.filename, + filename }); + if (processed && processed.dependencies) dependencies.push(...processed.dependencies); source = processed ? processed.code : source; } - if (options.style || options.script) { + + for (const fn of script) { + source = await replaceAsync( + source, + /([^]*?)<\/script>/gi, + async (match, attributes, content) => { + const processed: Processed = await fn({ + content, + attributes: parseAttributes(attributes), + filename + }); + if (processed && processed.dependencies) dependencies.push(...processed.dependencies); + return processed ? `${processed.code}` : match; + } + ); + } + + for (const fn of style) { source = await replaceAsync( source, - /<(script|style)([^]*?)>([^]*?)<\/\1>/gi, - async (match, type, attributes, content) => { - const processed: Processed = - type in options && - (await options[type]({ - content, - attributes: parseAttributes(attributes), - filename: options.filename, - })); - return processed ? `<${type}${attributes}>${processed.code}` : match; + /([^]*?)<\/style>/gi, + async (match, attributes, content) => { + const processed: Processed = await fn({ + content, + attributes: parseAttributes(attributes), + filename + }); + if (processed && processed.dependencies) dependencies.push(...processed.dependencies); + return processed ? `${processed.code}` : match; } ); } @@ -71,6 +98,9 @@ export default async function preprocess( // script { code: scriptCode, map: scriptMap }, // markup { code: markupCode, map: markupMap }, + code: source, + dependencies: [...new Set(dependencies)], + toString() { return source; } diff --git a/test/preprocess/index.js b/test/preprocess/index.js index 9d1e8b01c0..e24c96a0c4 100644 --- a/test/preprocess/index.js +++ b/test/preprocess/index.js @@ -16,10 +16,12 @@ describe.only('preprocess', () => { const input = fs.readFileSync(`test/preprocess/samples/${dir}/input.html`, 'utf-8'); const expected = fs.readFileSync(`test/preprocess/samples/${dir}/output.html`, 'utf-8'); - const actual = await svelte.preprocess(input, config.preprocess); - fs.writeFileSync(`test/preprocess/samples/${dir}/_actual.html`, actual); + const result = await svelte.preprocess(input, config.preprocess); + fs.writeFileSync(`test/preprocess/samples/${dir}/_actual.html`, result.code); - assert.equal(actual, expected); + assert.equal(result.code, expected); + + assert.deepEqual(result.dependencies, config.dependencies || []); }); }); }); diff --git a/test/preprocess/samples/dependencies/_config.js b/test/preprocess/samples/dependencies/_config.js new file mode 100644 index 0000000000..fa2d8b40c9 --- /dev/null +++ b/test/preprocess/samples/dependencies/_config.js @@ -0,0 +1,15 @@ +export default { + preprocess: { + style: ({ content }) => { + const dependencies = []; + const code = content.replace(/@import '(.+)';/g, (match, $1) => { + dependencies.push($1); + return '/* removed */'; + }); + + return { code, dependencies }; + } + }, + + dependencies: ['./foo.css'] +}; \ No newline at end of file diff --git a/test/preprocess/samples/dependencies/input.html b/test/preprocess/samples/dependencies/input.html new file mode 100644 index 0000000000..34a12faff6 --- /dev/null +++ b/test/preprocess/samples/dependencies/input.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/dependencies/output.html b/test/preprocess/samples/dependencies/output.html new file mode 100644 index 0000000000..d80d51a6fc --- /dev/null +++ b/test/preprocess/samples/dependencies/output.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/test/preprocess/samples/multiple-preprocessors/_config.js b/test/preprocess/samples/multiple-preprocessors/_config.js new file mode 100644 index 0000000000..8a57567f6b --- /dev/null +++ b/test/preprocess/samples/multiple-preprocessors/_config.js @@ -0,0 +1,14 @@ +export default { + preprocess: [ + { + markup: ({ content }) => ({ code: content.replace(/one/g, 'two') }), + script: ({ content }) => ({ code: content.replace(/three/g, 'four') }), + style: ({ content }) => ({ code: content.replace(/four/g, 'five') }) + }, + { + markup: ({ content }) => ({ code: content.replace(/two/g, 'three') }), + script: ({ content }) => ({ code: content.replace(/four/g, 'five') }), + style: ({ content }) => ({ code: content.replace(/three/g, 'four') }) + } + ] +}; \ No newline at end of file diff --git a/test/preprocess/samples/multiple-preprocessors/input.html b/test/preprocess/samples/multiple-preprocessors/input.html new file mode 100644 index 0000000000..6dbbfb47a9 --- /dev/null +++ b/test/preprocess/samples/multiple-preprocessors/input.html @@ -0,0 +1,11 @@ +

one

+ + + + \ No newline at end of file diff --git a/test/preprocess/samples/multiple-preprocessors/output.html b/test/preprocess/samples/multiple-preprocessors/output.html new file mode 100644 index 0000000000..dd6f3e9de8 --- /dev/null +++ b/test/preprocess/samples/multiple-preprocessors/output.html @@ -0,0 +1,11 @@ +

three

+ + + + \ No newline at end of file From b18aac97f741df5ea20324cc9d90cf5d54fe25b6 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 13 Jan 2019 19:30:21 -0500 Subject: [PATCH 3/3] reenable all tests --- test/preprocess/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/preprocess/index.js b/test/preprocess/index.js index e24c96a0c4..fddc14ec94 100644 --- a/test/preprocess/index.js +++ b/test/preprocess/index.js @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as assert from 'assert'; import { loadConfig, svelte } from '../helpers.js'; -describe.only('preprocess', () => { +describe('preprocess', () => { fs.readdirSync('test/preprocess/samples').forEach(dir => { if (dir[0] === '.') return;