diff --git a/mocha.opts b/mocha.opts deleted file mode 100644 index 427b029758..0000000000 --- a/mocha.opts +++ /dev/null @@ -1 +0,0 @@ -test/test.js \ No newline at end of file diff --git a/package.json b/package.json index ce4ee88ad3..f8fdfa1afe 100644 --- a/package.json +++ b/package.json @@ -13,19 +13,18 @@ "store", "animate", "transition", + "interpolate", "easing", "motion", "svelte", "README.md" ], - "engines": { - "node": ">= 8" - }, "types": "types/runtime/index.d.ts", "scripts": { - "test": "mocha --opts mocha.opts", - "test:unit": "mocha --require sucrase/register --recursive src/**/__test__.ts", - "quicktest": "mocha --opts mocha.opts", + "test": "mocha", + "test:unit": "mocha -r sucrase/register --recursive src/**/__test__.ts", + "quicktest": "mocha", + "update-expected": "node -r sucrase/register ./test/js/update.ts && node -r sucrase/register ./test/parser/update.ts", "precoverage": "c8 mocha --opts mocha.coverage.opts", "coverage": "c8 report --reporter=text-lcov > coverage.lcov && c8 report --reporter=html", "codecov": "codecov", @@ -39,6 +38,12 @@ "tsd": "tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly", "lint": "eslint \"{src,test}/**/*.{ts,js}\"" }, + "mocha": { + "file": "./test/test.ts", + "require": "sucrase/register", + "bail": true, + "timeout": "10000" + }, "repository": { "type": "git", "url": "https://github.com/sveltejs/svelte.git" @@ -56,41 +61,44 @@ }, "homepage": "https://github.com/sveltejs/svelte#README", "devDependencies": { - "@rollup/plugin-commonjs": "^11.0.0", - "@rollup/plugin-json": "^4.0.1", - "@rollup/plugin-node-resolve": "^6.0.0", - "@rollup/plugin-replace": "^2.3.0", - "@rollup/plugin-sucrase": "^3.0.0", - "@rollup/plugin-typescript": "^2.0.1", - "@rollup/plugin-virtual": "^2.0.0", - "@types/mocha": "^5.2.7", - "@types/node": "^8.10.53", - "@typescript-eslint/eslint-plugin": "^1.13.0", - "@typescript-eslint/parser": "^2.1.0", - "acorn": "^7.1.0", - "agadoo": "^1.1.0", - "c8": "^5.0.1", + "@rollup/plugin-commonjs": "^11.1.0", + "@rollup/plugin-json": "^4.0.3", + "@rollup/plugin-node-resolve": "^7.1.3", + "@rollup/plugin-replace": "^2.3.2", + "@rollup/plugin-sucrase": "^3.0.1", + "@rollup/plugin-typescript": "^4.1.1", + "@rollup/plugin-virtual": "^2.0.2", + "@types/jsdom": "^16.2.2", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.1", + "@types/puppeteer": "^2.1.0", + "@typescript-eslint/eslint-plugin": "^2.33.0", + "@typescript-eslint/parser": "^2.33.0", + "acorn": "^7.2.0", + "agadoo": "^2.0.0", + "c8": "^7.1.2", "code-red": "0.1.1", - "codecov": "^3.5.0", + "codecov": "^3.6.5", "css-tree": "1.0.0-alpha22", - "eslint": "^6.3.0", - "eslint-plugin-import": "^2.18.2", + "eslint-plugin-import": "^2.20.2", "eslint-plugin-svelte3": "^2.7.3", - "estree-walker": "^1.0.0", + "eslint": "^7.0.0", + "esm": "^3.2.25", + "estree-walker": "^2.0.1", "is-reference": "^1.1.4", - "jsdom": "^15.1.1", + "jsdom": "^16.2.2", "kleur": "^3.0.3", "locate-character": "^2.0.5", "magic-string": "^0.25.3", - "mocha": "^6.2.0", + "mocha": "^7.1.2", "periscopic": "^2.0.1", - "puppeteer": "^1.19.0", - "rollup": "^1.27.14", + "puppeteer": "^3.1.0", + "rollup": "^2.10.5", + "source-map-support": "^0.5.19", "source-map": "^0.7.3", - "source-map-support": "^0.5.13", - "tiny-glob": "^0.2.6", - "tslib": "^1.10.0", - "typescript": "^3.5.3" + "ts-mocha": "^7.0.0", + "tslib": "^2.0.0", + "typescript": "^3.9.2" }, "nyc": { "include": [ diff --git a/rollup.config.js b/rollup.config.js index 6f3d893a33..1bdc926d3c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -65,7 +65,7 @@ export default [ }), ts_plugin, { - writeBundle(bundle) { + writeBundle(_options, bundle) { if (dir === 'internal') { const mod = bundle['index.mjs']; if (mod) { diff --git a/test/.eslintrc.json b/test/.eslintrc.json index d5ba8f9d9c..fc5735de83 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -1,5 +1,7 @@ { - "rules": { - "no-console": "off" - } + "rules": { + "no-console": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-namespace": "off" + } } diff --git a/test/ambient.ts b/test/ambient.ts new file mode 100644 index 0000000000..53d285204d --- /dev/null +++ b/test/ambient.ts @@ -0,0 +1,13 @@ +import jsdom from 'jsdom'; +export {}; +declare global { + namespace NodeJS { + interface Global { + document: Document; + window: jsdom.DOMWindow; + navigator: Navigator; + getComputedStyle: jsdom.DOMWindow['getComputedStyle']; + requestAnimationFrame: any; + } + } +} diff --git a/test/css/index.js b/test/css/index.ts similarity index 72% rename from test/css/index.js rename to test/css/index.ts index dc17314597..8f1545bb6b 100644 --- a/test/css/index.js +++ b/test/css/index.ts @@ -1,6 +1,6 @@ -import * as assert from 'assert'; -import * as fs from 'fs'; -import { env, svelte, setupHtmlEqual, shouldUpdateExpected } from '../helpers.js'; +import { assert } from '../test'; +import { readFileSync, writeFileSync, readdirSync } from 'fs'; +import { env, svelte, setupHtmlEqual, shouldUpdateExpected } from '../helpers'; function try_require(file) { try { @@ -13,12 +13,12 @@ function try_require(file) { } function normalize_warning(warning) { - warning.frame = warning.frame - .replace(/^\n/, '') - .replace(/^\t+/gm, '') - .replace(/\s+$/gm, ''); + warning.frame = warning.frame.replace(/^\n/, '').replace(/^\t+/gm, '').replace(/\s+$/gm, ''); delete warning.filename; delete warning.toString; + delete warning.start; + delete warning.end; + delete warning.pos; return warning; } @@ -26,7 +26,7 @@ function create(code) { const fn = new Function('module', 'exports', 'require', code); const module = { exports: {} }; - fn(module, module.exports, id => { + fn(module, module.exports, (id) => { if (id === 'svelte') return require('../../index.js'); if (id.startsWith('svelte/')) return require(id.replace('svelte', '../../')); @@ -41,7 +41,7 @@ describe('css', () => { setupHtmlEqual(); }); - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { + readdirSync(`${__dirname}/samples`).forEach((dir) => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test @@ -54,21 +54,13 @@ describe('css', () => { (solo ? it.only : skip ? it.skip : it)(dir, () => { const config = try_require(`./samples/${dir}/_config.js`) || {}; - const input = fs - .readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8') - .replace(/\s+$/, ''); + const input = readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8').replace(/\s+$/, ''); const expected_warnings = (config.warnings || []).map(normalize_warning); - const dom = svelte.compile( - input, - Object.assign(config.compileOptions || {}, { format: 'cjs' }) - ); + const dom = svelte.compile(input, Object.assign(config.compileOptions || {}, { format: 'cjs' })); - const ssr = svelte.compile( - input, - Object.assign(config.compileOptions || {}, { format: 'cjs', generate: 'ssr' }) - ); + const ssr = svelte.compile(input, Object.assign(config.compileOptions || {}, { format: 'cjs', generate: 'ssr' })); assert.equal(dom.css.code, ssr.css.code); @@ -78,18 +70,18 @@ describe('css', () => { assert.deepEqual(dom_warnings, ssr_warnings); assert.deepEqual(dom_warnings.map(normalize_warning), expected_warnings); - fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.css`, dom.css.code); + writeFileSync(`${__dirname}/samples/${dir}/_actual.css`, dom.css.code); const expected = { html: read(`${__dirname}/samples/${dir}/expected.html`), - css: read(`${__dirname}/samples/${dir}/expected.css`) + css: read(`${__dirname}/samples/${dir}/expected.css`), }; - const actual_css = dom.css.code.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz'); + const actual_css = dom.css.code.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => ($1 ? m : 'svelte-xyz')); try { assert.equal(actual_css, expected.css); } catch (error) { if (shouldUpdateExpected()) { - fs.writeFileSync(`${__dirname}/samples/${dir}/expected.css`, actual_css); + writeFileSync(`${__dirname}/samples/${dir}/expected.css`, actual_css); console.log(`Updated ${dir}/expected.css.`); } else { throw error; @@ -126,9 +118,9 @@ describe('css', () => { new ClientComponent({ target, props: config.props }); const html = target.innerHTML; - fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html); + writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html); - const actual_html = html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz'); + const actual_html = html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => ($1 ? m : 'svelte-xyz')); assert.htmlEqual(actual_html, expected.html); window.document.head.innerHTML = ''; // remove added styles @@ -139,7 +131,9 @@ describe('css', () => { // ssr try { - const actual_ssr = ServerComponent.render(config.props).html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => $1 ? m : 'svelte-xyz'); + const actual_ssr = ServerComponent.render(config.props).html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => + $1 ? m : 'svelte-xyz' + ); assert.htmlEqual(actual_ssr, expected.html); } catch (err) { console.log(ssr.js.code); @@ -152,8 +146,8 @@ describe('css', () => { function read(file) { try { - return fs.readFileSync(file, 'utf-8'); + return readFileSync(file, 'utf-8'); } catch (err) { return null; } -} \ No newline at end of file +} diff --git a/test/custom-elements/index.js b/test/custom-elements/index.ts similarity index 78% rename from test/custom-elements/index.js rename to test/custom-elements/index.ts index d68d3b94c5..9fa6851b2c 100644 --- a/test/custom-elements/index.js +++ b/test/custom-elements/index.ts @@ -1,10 +1,10 @@ import * as fs from 'fs'; import * as path from 'path'; import * as http from 'http'; -import { rollup } from 'rollup'; -import * as virtual from '@rollup/plugin-virtual'; -import * as puppeteer from 'puppeteer'; -import { addLineNumbers, loadConfig, loadSvelte } from "../helpers.js"; +const { rollup } = require('rollup'); +const virtual = require('@rollup/plugin-virtual'); +const puppeteer = require('puppeteer'); +import { addLineNumbers, loadConfig, loadSvelte } from '../helpers'; import { deepEqual } from 'assert'; const page = ` @@ -16,7 +16,7 @@ const page = ` const assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8'); -describe('custom-elements', function() { +describe('custom-elements', function () { this.timeout(10000); let svelte; @@ -53,7 +53,7 @@ describe('custom-elements', function() { await browser.close(); }); - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { if (dir[0] === '.') return; const solo = /\.solo$/.test(dir); @@ -84,20 +84,19 @@ describe('custom-elements', function() { if (id.endsWith('.svelte')) { const compiled = svelte.compile(code, { customElement: true, - dev: config.dev + dev: config.dev, }); - compiled.warnings.forEach(w => warnings.push(w)); + compiled.warnings.forEach((w) => warnings.push(w)); return compiled.js; } - } + }, }, - virtual({ - assert - }) - ] + assert, + }), + ], }); const result = await bundle.generate({ format: 'iife', name: 'test' }); @@ -109,7 +108,7 @@ describe('custom-elements', function() { console[type](...args); }); - page.on('error', error => { + page.on('error', (error) => { console.log('>>> an error happened'); console.error(error); }); @@ -124,13 +123,16 @@ describe('custom-elements', function() { throw err; } finally { if (expected_warnings) { - deepEqual(warnings.map(w => ({ - code: w.code, - message: w.message, - pos: w.pos, - start: w.start, - end: w.end - })), expected_warnings); + deepEqual( + warnings.map((w) => ({ + code: w.code, + message: w.message, + pos: w.pos, + start: w.start, + end: w.end, + })), + expected_warnings + ); } } }); diff --git a/test/helpers.js b/test/helpers.ts similarity index 75% rename from test/helpers.js rename to test/helpers.ts index a764d43f96..3197779b35 100644 --- a/test/helpers.js +++ b/test/helpers.ts @@ -1,13 +1,13 @@ import * as jsdom from 'jsdom'; -import * as assert from 'assert'; -import * as glob from 'tiny-glob/sync.js'; import * as path from 'path'; import * as fs from 'fs'; import * as colors from 'kleur'; +import { glob } from './tiny-glob'; +import { assert } from './test'; // for coverage purposes, we need to test source files, // but for sanity purposes, we need to test dist files -export function loadSvelte(test) { +export function loadSvelte(test?) { process.env.TEST = test ? 'true' : ''; const resolved = require.resolve('../compiler.js'); @@ -16,7 +16,7 @@ export function loadSvelte(test) { return require(resolved); } -export const svelte = loadSvelte(); +export const svelte = loadSvelte(false); export function exists(path) { try { @@ -47,14 +47,15 @@ export function tryToReadFile(file) { export function cleanRequireCache() { Object.keys(require.cache) - .filter(x => x.endsWith('.svelte')) - .forEach(file => delete require.cache[file]); + .filter((x) => x.endsWith('.svelte')) + .forEach((file) => delete require.cache[file]); } const virtualConsole = new jsdom.VirtualConsole(); virtualConsole.sendTo(console); -const window = new jsdom.JSDOM('', {virtualConsole}).window; +const { window } = new jsdom.JSDOM('', { virtualConsole }); + global.document = window.document; global.navigator = window.navigator; global.getComputedStyle = window.getComputedStyle; @@ -63,11 +64,11 @@ global.window = window; // add missing ecmascript globals to window for (const key of Object.getOwnPropertyNames(global)) { - window[key] = window[key] || global[key]; + if (!(key in window)) window[key] = global[key]; } // implement mock scroll -window.scrollTo = function(pageXOffset, pageYOffset) { +window.scrollTo = function (pageXOffset, pageYOffset) { window.pageXOffset = pageXOffset; window.pageYOffset = pageYOffset; }; @@ -79,8 +80,8 @@ export function env() { return window; } - -function cleanChildren(node) { +const is_TextNode = (n: any): n is Text => n.nodeType === 3; +function cleanChildren(node: Element) { let previous = null; // sort attributes @@ -88,23 +89,18 @@ function cleanChildren(node) { return a.name < b.name ? -1 : 1; }); - attributes.forEach(attr => { + attributes.forEach((attr) => { node.removeAttribute(attr.name); }); - attributes.forEach(attr => { + attributes.forEach((attr) => { node.setAttribute(attr.name, attr.value); }); // recurse - [...node.childNodes].forEach(child => { - if (child.nodeType === 3) { - // text - if ( - node.namespaceURI === 'http://www.w3.org/2000/svg' && - node.tagName !== 'text' && - node.tagName !== 'tspan' - ) { + Array.from(node.childNodes).forEach((child) => { + if (is_TextNode(child)) { + if (node.namespaceURI === 'http://www.w3.org/2000/svg' && node.tagName !== 'text' && node.tagName !== 'tspan') { node.removeChild(child); } @@ -118,19 +114,19 @@ function cleanChildren(node) { child = previous; } } else { - cleanChildren(child); + cleanChildren(child as Element); } previous = child; }); // collapse whitespace - if (node.firstChild && node.firstChild.nodeType === 3) { + if (node.firstChild && is_TextNode(node.firstChild)) { node.firstChild.data = node.firstChild.data.replace(/^\s+/, ''); if (!node.firstChild.data) node.removeChild(node.firstChild); } - if (node.lastChild && node.lastChild.nodeType === 3) { + if (node.lastChild && is_TextNode(node.lastChild)) { node.lastChild.data = node.lastChild.data.replace(/\s+$/, ''); if (!node.lastChild.data) node.removeChild(node.lastChild); } @@ -149,16 +145,11 @@ export function normalizeHtml(window, html) { throw new Error(`Failed to normalize HTML:\n${html}`); } } - export function setupHtmlEqual() { const window = env(); - assert.htmlEqual = (actual, expected, message) => { - assert.deepEqual( - normalizeHtml(window, actual), - normalizeHtml(window, expected), - message - ); + assert.htmlEqual = function (actual, expected, message) { + assert.deepEqual(normalizeHtml(window, actual), normalizeHtml(window, expected), message); }; } @@ -184,28 +175,25 @@ export function addLineNumbers(code) { .map((line, i) => { i = String(i + 1); while (i.length < 3) i = ` ${i}`; - - return ( - colors.gray(` ${i}: `) + - line.replace(/^\t+/, match => match.split('\t').join(' ')) - ); + return colors.gray(` ${i}: `) + line.replace(/^\t+/, (match) => match.split('\t').join(' ')); }) .join('\n'); } export function showOutput(cwd, options = {}, compile = svelte.compile) { - glob('**/*.svelte', { cwd }).forEach(file => { + glob('**/*.svelte', { cwd }).forEach((file) => { if (file[0] === '_') return; try { const { js } = compile( fs.readFileSync(`${cwd}/${file}`, 'utf-8'), Object.assign(options, { - filename: file + filename: file, }) ); - console.log( // eslint-disable-line no-console + console.log( + // eslint-disable-line no-console `\n>> ${colors.cyan().bold(file)}\n${addLineNumbers(js.code)}\n<< ${colors.cyan().bold(file)}` ); } catch (err) { @@ -218,31 +206,25 @@ export function shouldUpdateExpected() { return process.argv.includes('--update'); } -export function spaces(i) { - let result = ''; - while (i--) result += ' '; - return result; -} - // fake timers const original_set_timeout = global.setTimeout; export function useFakeTimers() { const callbacks = []; - global.setTimeout = function(fn) { + global.setTimeout = function (fn) { callbacks.push(fn); - }; + } as (callback: (...args: any[]) => void, ms: number, ...args: any[]) => any; return { flush() { - callbacks.forEach(fn => fn()); + callbacks.forEach((fn) => fn()); callbacks.splice(0, callbacks.length); }, removeFakeTimers() { callbacks.splice(0, callbacks.length); global.setTimeout = original_set_timeout; - } + }, }; } @@ -257,4 +239,4 @@ export function mkdirp(dir) { } catch (err) { // do nothing } -} \ No newline at end of file +} diff --git a/test/hydration/index.js b/test/hydration/index.ts similarity index 81% rename from test/hydration/index.js rename to test/hydration/index.ts index f57a0cdc1a..a5262c1b99 100644 --- a/test/hydration/index.js +++ b/test/hydration/index.ts @@ -1,15 +1,8 @@ -import * as assert from 'assert'; import * as path from 'path'; import * as fs from 'fs'; -import { - showOutput, - loadConfig, - loadSvelte, - env, - setupHtmlEqual, - shouldUpdateExpected -} from '../helpers.js'; +import { showOutput, loadConfig, loadSvelte, env, setupHtmlEqual, shouldUpdateExpected } from '../helpers'; +import { assert } from '../test'; let compileOptions = null; @@ -19,13 +12,13 @@ describe('hydration', () => { before(() => { const svelte = loadSvelte(); - require.extensions['.svelte'] = function(module, filename) { + require.extensions['.svelte'] = function (module, filename) { const options = Object.assign( { filename, hydratable: true, format: 'cjs', - sveltePath + sveltePath, }, compileOptions ); @@ -58,13 +51,11 @@ describe('hydration', () => { try { global.window = window; - let SvelteComponent; - - try { - SvelteComponent = require(`${cwd}/main.svelte`).default; - } catch (err) { - throw err; - } + // try { + const SvelteComponent = require(`${cwd}/main.svelte`).default; + // } catch (err) { + // throw err; + // } const target = window.document.body; const head = window.document.head; @@ -75,14 +66,16 @@ describe('hydration', () => { try { before_head = fs.readFileSync(`${cwd}/_before_head.html`, 'utf-8'); head.innerHTML = before_head; - } catch (err) {} + } catch (_err) { + // + } const snapshot = config.snapshot ? config.snapshot(target) : {}; const component = new SvelteComponent({ target, hydrate: true, - props: config.props + props: config.props, }); try { @@ -117,18 +110,19 @@ describe('hydration', () => { } } catch (err) { showOutput(cwd, { - hydratable: true + hydratable: true, }); throw err; } - if (config.show) showOutput(cwd, { - hydratable: true - }); + if (config.show) + showOutput(cwd, { + hydratable: true, + }); }); } - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { runTest(dir, null); }); }); diff --git a/test/js/index.js b/test/js/index.ts similarity index 52% rename from test/js/index.js rename to test/js/index.ts index d4ea13ad98..714c9b9b6f 100644 --- a/test/js/index.js +++ b/test/js/index.ts @@ -1,38 +1,46 @@ -import * as assert from "assert"; -import * as fs from "fs"; -import * as path from "path"; -import * as colors from "kleur"; -import { loadConfig, svelte, shouldUpdateExpected } from "../helpers.js"; +import * as fs from 'fs'; +import * as path from 'path'; +import * as colors from 'kleur'; +import { loadConfig, svelte, shouldUpdateExpected } from '../helpers'; +import { assert } from '../test'; -describe("js", () => { - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { - if (dir[0] === ".") return; +describe('js', () => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { + if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test const solo = /\.solo/.test(dir); if (solo && process.env.CI) { - throw new Error("Forgot to remove `solo: true` from test"); + throw new Error('Forgot to remove `solo: true` from test'); } const resolved = path.resolve(`${__dirname}/samples`, dir); if (!fs.existsSync(`${resolved}/input.svelte`)) { - console.log(colors.red().bold(`Missing file ${dir}/input.svelte. If you recently switched branches you may need to delete this directory`)); + console.log( + colors + .red() + .bold( + `Missing file ${dir}/input.svelte. If you recently switched branches you may need to delete this directory` + ) + ); return; } (solo ? it.only : it)(dir, () => { const config = loadConfig(`${resolved}/_config.js`); - const input = fs.readFileSync(`${resolved}/input.svelte`, "utf-8").replace(/\s+$/, ""); + const input = fs.readFileSync(`${resolved}/input.svelte`, 'utf-8').replace(/\s+$/, ''); let actual; try { const options = Object.assign(config.options || {}); - actual = svelte.compile(input, options).js.code.replace(/generated by Svelte v\d+\.\d+\.\d+(-\w+\.\d+)?/, 'generated by Svelte vX.Y.Z'); + actual = svelte + .compile(input, options) + .js.code.replace(/generated by Svelte v\d+\.\d+\.\d+(-\w+\.\d+)?/, 'generated by Svelte vX.Y.Z'); } catch (err) { console.log(err.frame); throw err; @@ -45,7 +53,7 @@ describe("js", () => { let expected = ''; try { - expected = fs.readFileSync(expectedPath, "utf-8"); + expected = fs.readFileSync(expectedPath, 'utf-8'); } catch (error) { console.log(error); if (error.code === 'ENOENT') { @@ -55,10 +63,7 @@ describe("js", () => { } try { - assert.equal( - actual.trim().replace(/^[ \t]+$/gm, ""), - expected.trim().replace(/^[ \t]+$/gm, "") - ); + assert.equal(actual.trim().replace(/^[ \t]+$/gm, ''), expected.trim().replace(/^[ \t]+$/gm, '')); } catch (error) { if (shouldUpdateExpected()) { fs.writeFileSync(expectedPath, actual); diff --git a/test/js/update.js b/test/js/update.js deleted file mode 100644 index fa9c834182..0000000000 --- a/test/js/update.js +++ /dev/null @@ -1,13 +0,0 @@ -// this file will replace all the expected.js files with their _actual -// equivalents. Only use it when you're sure that you haven't -// broken anything! -const fs = require("fs"); -const glob = require("tiny-glob/sync.js"); - -glob("samples/*/_actual.js", { cwd: __dirname }).forEach(file => { - const actual = fs.readFileSync(`${__dirname}/${file}`, "utf-8"); - fs.writeFileSync( - `${__dirname}/${file.replace("_actual.js", "expected.js")}`, - actual - ); -}); diff --git a/test/js/update.ts b/test/js/update.ts new file mode 100644 index 0000000000..d2f95b99bf --- /dev/null +++ b/test/js/update.ts @@ -0,0 +1,39 @@ +import { readFileSync, writeFileSync } from 'fs'; +import { resolve } from 'path'; +// this file will replace all the expected.js files with their _actual +// equivalents. Only use it when you're sure that you haven't +// broken anything! +const svelte = (function loadSvelte(test) { + process.env.TEST = test ? 'true' : ''; + const resolved = require.resolve('../../compiler.js'); + delete require.cache[resolved]; + return require(resolved); +})(false); +function loadConfig(file) { + try { + const resolved = require.resolve(file); + delete require.cache[resolved]; + + const config = require(resolved); + return config.default || config; + } catch (err) { + if (err.code === 'MODULE_NOT_FOUND') { + return {}; + } + + throw err; + } +} +require(resolve(__dirname, '../tiny-glob.ts')) + .glob('samples/*/_actual.js', { cwd: __dirname }) + .forEach((file) => { + writeFileSync( + `${__dirname}/${file.replace('_actual.js', 'expected.js')}`, + svelte + .compile( + readFileSync(`${__dirname}/${file.replace('_actual.js', 'input.svelte')}`, 'utf-8').replace(/\s+$/, ''), + loadConfig(`${__dirname}/${file.replace('_actual.js', '_config.js')}`).options + ) + .js.code.replace(/generated by Svelte v\d+\.\d+\.\d+(-\w+\.\d+)?/, 'generated by Svelte vX.Y.Z') + ); + }); diff --git a/test/motion/index.js b/test/motion/index.ts similarity index 89% rename from test/motion/index.js rename to test/motion/index.ts index ec58c6df36..220e234ee5 100644 --- a/test/motion/index.js +++ b/test/motion/index.ts @@ -1,4 +1,4 @@ -import * as assert from 'assert'; +import { assert } from '../test'; import { get } from '../../store'; import { spring, tweened } from '../../motion'; @@ -23,7 +23,7 @@ describe('motion', () => { it('sets immediately when duration is 0', () => { const size = tweened(0); - size.set(100, { duration : 0 }); + size.set(100, { duration: 0 }); assert.equal(get(size), 100); }); }); diff --git a/test/parser/index.js b/test/parser/index.ts similarity index 53% rename from test/parser/index.js rename to test/parser/index.ts index 27c5ec1563..f5590834e2 100644 --- a/test/parser/index.js +++ b/test/parser/index.ts @@ -1,18 +1,16 @@ -import * as assert from 'assert'; import * as fs from 'fs'; -import { svelte, tryToLoadJson, shouldUpdateExpected } from '../helpers.js'; +import { svelte, tryToLoadJson } from '../helpers'; +import { assert } from '../test'; -describe('parse', () => { - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { +describe('parser', () => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test const solo = /\.solo$/.test(dir); if (solo && process.env.CI) { - throw new Error( - `Forgot to remove '.solo' from test parser/samples/${dir}` - ); + throw new Error(`Forgot to remove '.solo' from test parser/samples/${dir}`); } const skip = !fs.existsSync(`${__dirname}/samples/${dir}/input.svelte`); @@ -25,9 +23,12 @@ describe('parse', () => { const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`); try { - const { ast } = svelte.compile(input, Object.assign(options, { - generate: false - })); + const { ast } = svelte.compile( + input, + Object.assign(options, { + generate: false, + }) + ); fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.json`, JSON.stringify(ast, null, '\t')); @@ -36,19 +37,8 @@ describe('parse', () => { assert.deepEqual(ast.instance, expectedOutput.instance); assert.deepEqual(ast.module, expectedOutput.module); } catch (err) { - if (err.name !== 'ParseError') throw err; - if (!expectedError) throw err; - - try { - assert.equal(err.code, expectedError.code); - assert.equal(err.message, expectedError.message); - assert.deepEqual(err.start, expectedError.start); - assert.equal(err.pos, expectedError.pos); - assert.equal(err.toString().split('\n')[0], `${expectedError.message} (${expectedError.start.line}:${expectedError.start.column})`); - } catch (err2) { - const e = err2.code === 'MODULE_NOT_FOUND' ? err : err2; - throw e; - } + if (err.name !== 'ParseError' || !expectedError) throw err; + assert.deepEqual(JSON.parse(JSON.stringify({ ...err, message: err.message })), expectedError); } }); }); diff --git a/test/parser/update.js b/test/parser/update.js deleted file mode 100644 index 0edac895b5..0000000000 --- a/test/parser/update.js +++ /dev/null @@ -1,13 +0,0 @@ -// this file will replace all the output.json files with their _actual.json -// equivalents. Only use it when you're sure that you haven't -// broken anything! -const fs = require("fs"); -const glob = require("tiny-glob/sync.js"); - -glob("samples/*/_actual.json", { cwd: __dirname }).forEach(file => { - const actual = fs.readFileSync(`${__dirname}/${file}`, "utf-8"); - fs.writeFileSync( - `${__dirname}/${file.replace("_actual.json", "output.json")}`, - actual - ); -}); diff --git a/test/parser/update.ts b/test/parser/update.ts new file mode 100644 index 0000000000..c08e244f0c --- /dev/null +++ b/test/parser/update.ts @@ -0,0 +1,31 @@ +import { readFileSync, writeFileSync } from 'fs'; +import { resolve } from 'path'; +// this file will replace all the expected.js files with their _actual +// equivalents. Only use it when you're sure that you haven't +// broken anything! +const svelte = (function loadSvelte(test) { + process.env.TEST = test ? 'true' : ''; + const resolved = require.resolve('../../compiler.js'); + delete require.cache[resolved]; + return require(resolved); +})(false); +require(resolve(__dirname, '../tiny-glob.ts')) + .glob('samples/*/input.svelte', { cwd: __dirname }) + .forEach((file) => { + try { + writeFileSync( + `${__dirname}/${file.replace('input.svelte', 'output.json')}`, + JSON.stringify( + svelte.compile(readFileSync(`${__dirname}/${file}`, 'utf-8').replace(/\s+$/, ''), { generate: false }).ast, + null, + '\t' + ) + ); + } catch (e) { + if (e.name !== 'ParseError') throw e; + writeFileSync( + `${__dirname}/${file.replace('input.svelte', 'error.json')}`, + JSON.stringify({ ...e, message: e.message }, null, '\t') + ); + } + }); diff --git a/test/preprocess/index.js b/test/preprocess/index.ts similarity index 85% rename from test/preprocess/index.js rename to test/preprocess/index.ts index 5d83bb6059..758c617eb8 100644 --- a/test/preprocess/index.js +++ b/test/preprocess/index.ts @@ -1,9 +1,9 @@ import * as fs from 'fs'; -import * as assert from 'assert'; -import { loadConfig, svelte } from '../helpers.js'; +import { loadConfig, svelte } from '../helpers'; +import { assert } from '../test'; describe('preprocess', () => { - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { if (dir[0] === '.') return; const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); diff --git a/test/runtime/index.js b/test/runtime/index.ts similarity index 53% rename from test/runtime/index.js rename to test/runtime/index.ts index f070eb8185..8247c117df 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.ts @@ -1,20 +1,11 @@ -import * as assert from "assert"; -import * as path from "path"; -import * as fs from "fs"; +import { relative, resolve } from 'path'; +import { readFileSync, existsSync, unlinkSync, writeFileSync, readdirSync } from 'fs'; import { rollup } from 'rollup'; -import * as virtual from '@rollup/plugin-virtual'; -import * as glob from 'tiny-glob/sync.js'; -import { clear_loops, flush, set_now, set_raf } from "../../internal"; - -import { - showOutput, - loadConfig, - loadSvelte, - cleanRequireCache, - env, - setupHtmlEqual, - mkdirp -} from "../helpers.js"; +import virtual from '@rollup/plugin-virtual'; +import { clear_loops, flush, SvelteComponent,set_now, set_raf } from '../../internal'; +import { showOutput, loadConfig, loadSvelte, cleanRequireCache, env, setupHtmlEqual, mkdirp } from '../helpers'; +import { glob } from '../tiny-glob'; +import { assert } from '../test'; let svelte$; let svelte; @@ -24,24 +15,21 @@ let compile = null; const sveltePath = process.cwd().split('\\').join('/'); -let unhandled_rejection = false; -process.on('unhandledRejection', err => { +let unhandled_rejection: Error | false = false; +process.on('unhandledRejection', (err: Error) => { unhandled_rejection = err; }); -describe("runtime", () => { +describe('runtime', () => { before(() => { svelte = loadSvelte(false); svelte$ = loadSvelte(true); - require.extensions[".svelte"] = function(module, filename) { - const options = Object.assign({ + require.extensions['.svelte'] = function (module, filename) { + return module._compile( + compile(readFileSync(filename, 'utf-8'), { filename, ...compileOptions }).js.code, filename - }, compileOptions); - - const { js: { code } } = compile(fs.readFileSync(filename, "utf-8"), options); - - return module._compile(code, filename); + ); }; return setupHtmlEqual(); @@ -49,19 +37,20 @@ describe("runtime", () => { const failed = new Set(); - function runTest(dir, hydrate) { - if (dir[0] === ".") return; + function runTest(dir, hydratable) { + if (dir[0] === '.') return; const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); const solo = config.solo || /\.solo/.test(dir); + const skip = config.skip || /\.skip/.test(dir); - if (hydrate && config.skip_if_hydrate) return; + if (hydratable && config.skip_if_hydrate) return; if (solo && process.env.CI) { - throw new Error("Forgot to remove `solo: true` from test"); + throw new Error('Forgot to remove `solo: true` from test'); } - (config.skip ? it.skip : solo ? it.only : it)(`${dir} ${hydrate ? '(with hydration)' : ''}`, () => { + (skip ? it.skip : solo ? it.only : it)(`${dir} ${hydratable ? '(with hydration)' : ''}`, () => { if (failed.has(dir)) { // this makes debugging easier, by only printing compiled output once throw new Error('skipping test, already failed'); @@ -69,16 +58,18 @@ describe("runtime", () => { unhandled_rejection = null; - compile = (config.preserveIdentifiers ? svelte : svelte$).compile; + ({ compile } = config.preserveIdentifiers ? svelte : svelte$); - const cwd = path.resolve(`${__dirname}/samples/${dir}`); + const cwd = resolve(`${__dirname}/samples/${dir}`); - compileOptions = config.compileOptions || {}; - compileOptions.format = 'cjs'; - compileOptions.sveltePath = sveltePath; - compileOptions.hydratable = hydrate; - compileOptions.immutable = config.immutable; - compileOptions.accessors = 'accessors' in config ? config.accessors : true; + compileOptions = { + ...(config.compileOptions || {}), + format: 'cjs', + sveltePath, + hydratable, + immutable: config.immutable, + accessors: 'accessors' in config ? config.accessors : true, + }; cleanRequireCache(); @@ -89,28 +80,23 @@ describe("runtime", () => { const window = env(); - glob('**/*.svelte', { cwd }).forEach(file => { - if (file[0] === '_') return; - - const dir = `${cwd}/_output/${hydrate ? 'hydratable' : 'normal'}`; - const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`; + glob('**/*.svelte', { cwd }).forEach((filename) => { + if (filename[0] === '_') return; - if (fs.existsSync(out)) { - fs.unlinkSync(out); - } + const dir = `${cwd}/_output/${hydratable ? 'hydratable' : 'normal'}`; + const out = `${dir}/${filename.replace(/\.svelte$/, '.js')}`; + if (existsSync(out)) unlinkSync(out); mkdirp(dir); try { - const { js } = compile( - fs.readFileSync(`${cwd}/${file}`, 'utf-8'), - { + writeFileSync( + out, + compile(readFileSync(`${cwd}/${filename}`, 'utf-8'), { ...compileOptions, - filename: file - } + filename, + }).js.code ); - - fs.writeFileSync(out, js.code); } catch (err) { // do nothing } @@ -118,19 +104,17 @@ describe("runtime", () => { return Promise.resolve() .then(() => { - // hack to support transition tests clear_loops(); - const raf = { time: 0, callback: null, - tick: now => { + tick: (now) => { raf.time = now; if (raf.callback) raf.callback(); - } + }, }; set_now(() => raf.time); - set_raf(cb => { + set_raf((cb) => { raf.callback = () => { raf.callback = null; cb(raf.time); @@ -139,8 +123,7 @@ describe("runtime", () => { }); try { - mod = require(`./samples/${dir}/main.svelte`); - SvelteComponent = mod.default; + SvelteComponent = (mod = require(`./samples/${dir}/main.svelte`)).default; } catch (err) { showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console throw err; @@ -151,35 +134,33 @@ describe("runtime", () => { // Put things we need on window for testing window.SvelteComponent = SvelteComponent; - const target = window.document.querySelector("main"); + const target = window.document.querySelector('main'); const warnings = []; const warn = console.warn; - console.warn = warning => { + console.warn = (warning) => { warnings.push(warning); }; - const options = Object.assign({}, { - target, - hydrate, - props: config.props, - intro: config.intro - }, config.options || {}); + const options = { + ...{ target, hydrate: hydratable, props: config.props, intro: config.intro }, + ...(config.options || {}), + }; - const component = new SvelteComponent(options); + const component: SvelteComponent = new SvelteComponent(options); console.warn = warn; if (config.error) { unintendedError = true; - throw new Error("Expected a runtime error"); + throw new Error('Expected a runtime error'); } if (config.warnings) { assert.deepEqual(warnings, config.warnings); } else if (warnings.length) { unintendedError = true; - throw new Error("Received unexpected warnings"); + throw new Error('Received unexpected warnings'); } if (config.html) { @@ -187,15 +168,18 @@ describe("runtime", () => { } if (config.test) { - return Promise.resolve(config.test({ - assert, - component, - mod, - target, - window, - raf, - compileOptions - })).then(() => { + return Promise.resolve( + config.test({ + assert, + component, + mod, + target, + window, + raf, + compileOptions, + }) + ).then(() => { + raf.tick(Infinity); component.$destroy(); if (unhandled_rejection) { @@ -204,14 +188,14 @@ describe("runtime", () => { }); } else { component.$destroy(); - assert.htmlEqual(target.innerHTML, ""); + assert.htmlEqual(target.innerHTML, ''); if (unhandled_rejection) { throw unhandled_rejection; } } }) - .catch(err => { + .catch((err) => { if (config.error && !unintendedError) { if (typeof config.error === 'function') { config.error(assert, err); @@ -221,14 +205,15 @@ describe("runtime", () => { } else { throw err; } - }).catch(err => { + }) + .catch((err) => { failed.add(dir); showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console throw err; }) - .catch(err => { + .catch((err) => { // print a clickable link to open the directory - err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`; + err.stack += `\n\ncmd-click: ${relative(process.cwd(), cwd)}/main.svelte`; throw err; }) .then(() => { @@ -243,23 +228,23 @@ describe("runtime", () => { }); } - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { + readdirSync(`${__dirname}/samples`).forEach((dir) => { runTest(dir, false); runTest(dir, true); }); async function create_component(src = '
') { const { js } = svelte$.compile(src, { - format: "esm", - name: "SvelteComponent", - dev: true + format: 'esm', + name: 'SvelteComponent', + dev: true, }); const bundle = await rollup({ input: 'main.js', plugins: [ virtual({ - 'main.js': js.code + 'main.js': js.code, }), { name: 'svelte-packages', @@ -267,22 +252,20 @@ describe("runtime", () => { if (importee.startsWith('svelte/')) { return importee.replace('svelte', process.cwd()) + '/index.mjs'; } - } - } - ] + }, + }, + ], }); const result = await bundle.generate({ format: 'iife', - name: 'App' + name: 'App', }); - return eval( - `(function () { ${result.output[0].code}; return App; }())` - ); + return eval(`(function () { ${result.output[0].code}; return App; }())`); } - it("fails if options.target is missing in dev mode", async () => { + it('fails if options.target is missing in dev mode', async () => { const App = await create_component(); assert.throws(() => { @@ -290,13 +273,13 @@ describe("runtime", () => { }, /'target' is a required option/); }); - it("fails if options.hydrate is true but the component is non-hydratable", async () => { + it('fails if options.hydrate is true but the component is non-hydratable', async () => { const App = await create_component(); assert.throws(() => { new App({ target: { childNodes: [] }, - hydrate: true + hydrate: true, }); }, /options.hydrate only works if the component was compiled with the `hydratable: true` option/); }); diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.ts similarity index 73% rename from test/server-side-rendering/index.js rename to test/server-side-rendering/index.ts index ee1319845d..341a8df3dd 100644 --- a/test/server-side-rendering/index.js +++ b/test/server-side-rendering/index.ts @@ -1,7 +1,5 @@ -import * as assert from "assert"; -import * as fs from "fs"; -import * as path from "path"; -import * as glob from 'tiny-glob/sync.js'; +import * as fs from 'fs'; +import * as path from 'path'; import { showOutput, @@ -11,14 +9,16 @@ import { tryToLoadJson, cleanRequireCache, shouldUpdateExpected, - mkdirp -} from "../helpers.js"; + mkdirp, +} from '../helpers'; +import { glob } from '../tiny-glob'; +import { assert } from '../test'; function tryToReadFile(file) { try { - return fs.readFileSync(file, "utf-8"); + return fs.readFileSync(file, 'utf-8'); } catch (err) { - if (err.code !== "ENOENT") throw err; + if (err.code !== 'ENOENT') throw err; return null; } } @@ -26,28 +26,29 @@ function tryToReadFile(file) { const sveltePath = process.cwd().split('\\').join('/'); let compile = null; -describe("ssr", () => { +describe('ssr', () => { before(() => { compile = loadSvelte(true).compile; return setupHtmlEqual(); }); - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { - if (dir[0] === ".") return; + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { + if (dir[0] === '.') return; const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); // add .solo to a sample directory name to only run that test, or // .show to always show the output. or both const solo = config.solo || /\.solo/.test(dir); + const skip = config.skip || /\.skip/.test(dir); const show = /\.show/.test(dir); if (solo && process.env.CI) { - throw new Error("Forgot to remove `solo: true` from test"); + throw new Error('Forgot to remove `solo: true` from test'); } - (solo ? it.only : it)(dir, () => { + (skip ? it.skip : solo ? it.only : it)(dir, () => { dir = path.resolve(`${__dirname}/samples`, dir); cleanRequireCache(); @@ -56,16 +57,16 @@ describe("ssr", () => { sveltePath, ...config.compileOptions, generate: 'ssr', - format: 'cjs' + format: 'cjs', }; - require("../../register")(compileOptions); + require('../../register')(compileOptions); try { const Component = require(`${dir}/main.svelte`).default; const expectedHtml = tryToReadFile(`${dir}/_expected.html`); - const expectedCss = tryToReadFile(`${dir}/_expected.css`) || ""; + const expectedCss = tryToReadFile(`${dir}/_expected.css`) || ''; const props = tryToLoadJson(`${dir}/data.json`) || undefined; @@ -87,10 +88,7 @@ describe("ssr", () => { } try { - assert.equal( - css.code.replace(/^\s+/gm, ""), - expectedCss.replace(/^\s+/gm, "") - ); + assert.equal(css.code.replace(/^\s+/gm, ''), expectedCss.replace(/^\s+/gm, '')); } catch (error) { if (shouldUpdateExpected()) { fs.writeFileSync(`${dir}/_expected.css`, css.code); @@ -104,10 +102,7 @@ describe("ssr", () => { fs.writeFileSync(`${dir}/_actual-head.html`, head); try { - assert.htmlEqual( - head, - fs.readFileSync(`${dir}/_expected-head.html`, 'utf-8') - ); + assert.htmlEqual(head, fs.readFileSync(`${dir}/_expected-head.html`, 'utf-8')); } catch (error) { if (shouldUpdateExpected()) { fs.writeFileSync(`${dir}/_expected-head.html`, head); @@ -128,20 +123,21 @@ describe("ssr", () => { }); // duplicate client-side tests, as far as possible - fs.readdirSync("test/runtime/samples").forEach(dir => { - if (dir[0] === ".") return; + fs.readdirSync('test/runtime/samples').forEach((dir) => { + if (dir[0] === '.') return; const config = loadConfig(`./runtime/samples/${dir}/_config.js`); const solo = config.solo || /\.solo/.test(dir); + const skip = config.skip || /\.skip/.test(dir); if (solo && process.env.CI) { - throw new Error("Forgot to remove `solo: true` from test"); + throw new Error('Forgot to remove `solo: true` from test'); } if (config.skip_if_ssr) return; - (config.skip ? it.skip : solo ? it.only : it)(dir, () => { - const cwd = path.resolve("test/runtime/samples", dir); + (skip ? it.skip : solo ? it.only : it)(dir, () => { + const cwd = path.resolve('test/runtime/samples', dir); cleanRequireCache(); @@ -151,15 +147,15 @@ describe("ssr", () => { sveltePath, ...config.compileOptions, generate: 'ssr', - format: 'cjs' + format: 'cjs', }; - require("../../register")(compileOptions); + require('../../register')(compileOptions); - glob('**/*.svelte', { cwd }).forEach(file => { + glob('**/*.svelte', { cwd }).forEach((file) => { if (file[0] === '_') return; - const dir = `${cwd}/_output/ssr`; + const dir = `${cwd}/_output/ssr`; const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`; if (fs.existsSync(out)) { @@ -169,13 +165,10 @@ describe("ssr", () => { mkdirp(dir); try { - const { js } = compile( - fs.readFileSync(`${cwd}/${file}`, 'utf-8'), - { - ...compileOptions, - filename: file - } - ); + const { js } = compile(fs.readFileSync(`${cwd}/${file}`, 'utf-8'), { + ...compileOptions, + filename: file, + }); fs.writeFileSync(out, js.code); } catch (err) { @@ -188,7 +181,7 @@ describe("ssr", () => { const Component = require(`../runtime/samples/${dir}/main.svelte`).default; const { html } = Component.render(config.props, { - store: (config.store !== true) && config.store + store: config.store !== true && config.store, }); if (config.ssrHtml) { diff --git a/test/setup.js b/test/setup.js deleted file mode 100644 index 7406a07dd9..0000000000 --- a/test/setup.js +++ /dev/null @@ -1,38 +0,0 @@ -const fs = require('fs'); - -require('source-map-support').install(); - -process.env.TEST = true; - -require.extensions['.js'] = function(module, filename) { - const exports = []; - - let code = fs.readFileSync(filename, 'utf-8') - .replace(/^import \* as (\w+) from ['"]([^'"]+)['"];?/gm, 'var $1 = require("$2");') - .replace(/^import (\w+) from ['"]([^'"]+)['"];?/gm, 'var {default: $1} = require("$2");') - .replace(/^import {([^}]+)} from ['"](.+)['"];?/gm, 'var {$1} = require("$2");') - .replace(/^export default /gm, 'exports.default = ') - .replace(/^export (const|let|var|class|function) (\w+)/gm, (match, type, name) => { - exports.push(name); - return `${type} ${name}`; - }) - .replace(/^export \{([^}]+)\}(?: from ['"]([^'"]+)['"];?)?/gm, (match, names, source) => { - names.split(',').filter(Boolean).forEach(name => { - exports.push(name); - }); - - return source ? `const { ${names} } = require("${source}");` : ''; - }) - .replace(/^export function (\w+)/gm, 'exports.$1 = function $1'); - - exports.forEach(name => { - code += `\nexports.${name} = ${name};`; - }); - - try { - return module._compile(code, filename); - } catch (err) { - console.log(code); // eslint-disable-line no-console - throw err; - } -}; diff --git a/test/sourcemaps/index.js b/test/sourcemaps/index.js deleted file mode 100644 index 0b0424a764..0000000000 --- a/test/sourcemaps/index.js +++ /dev/null @@ -1,73 +0,0 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as assert from "assert"; -import { svelte } from "../helpers.js"; -import { SourceMapConsumer } from "source-map"; -import { getLocator } from "locate-character"; - -describe("sourcemaps", () => { - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { - if (dir[0] === ".") return; - - // add .solo to a sample directory name to only run that test - const solo = /\.solo/.test(dir); - const skip = /\.skip/.test(dir); - - if (solo && process.env.CI) { - throw new Error("Forgot to remove `solo: true` from test"); - } - - (solo ? it.only : skip ? it.skip : it)(dir, async () => { - const filename = path.resolve( - `${__dirname}/samples/${dir}/input.svelte` - ); - const outputFilename = path.resolve( - `${__dirname}/samples/${dir}/output` - ); - - const input = fs.readFileSync(filename, "utf-8").replace(/\s+$/, ""); - const { js, css } = svelte.compile(input, { - filename, - outputFilename: `${outputFilename}.js`, - cssOutputFilename: `${outputFilename}.css` - }); - - const _code = js.code.replace(/Svelte v\d+\.\d+\.\d+/, match => match.replace(/\d/g, 'x')); - - fs.writeFileSync( - `${outputFilename}.js`, - `${_code}\n//# sourceMappingURL=output.js.map` - ); - fs.writeFileSync( - `${outputFilename}.js.map`, - JSON.stringify(js.map, null, " ") - ); - - if (css.code) { - fs.writeFileSync( - `${outputFilename}.css`, - `${css.code}\n/*# sourceMappingURL=output.css.map */` - ); - fs.writeFileSync( - `${outputFilename}.css.map`, - JSON.stringify(css.map, null, " ") - ); - } - - assert.deepEqual(js.map.sources, ["input.svelte"]); - if (css.map) assert.deepEqual(css.map.sources, ["input.svelte"]); - - const { test } = require(`./samples/${dir}/test.js`); - - const locateInSource = getLocator(input); - - const smc = await new SourceMapConsumer(js.map); - const locateInGenerated = getLocator(_code); - - const smcCss = css.map && await new SourceMapConsumer(css.map); - const locateInGeneratedCss = getLocator(css.code || ''); - - test({ assert, code: _code, map: js.map, smc, smcCss, locateInSource, locateInGenerated, locateInGeneratedCss }); - }); - }); -}); diff --git a/test/sourcemaps/index.ts b/test/sourcemaps/index.ts new file mode 100644 index 0000000000..fa27978497 --- /dev/null +++ b/test/sourcemaps/index.ts @@ -0,0 +1,57 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as assert from 'assert'; +import { svelte } from '../helpers'; +import { SourceMapConsumer } from 'source-map'; +import { getLocator } from 'locate-character'; + +describe('sourcemaps', () => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { + if (dir[0] === '.') return; + + // add .solo to a sample directory name to only run that test + const solo = /\.solo/.test(dir); + const skip = /\.skip/.test(dir); + + if (solo && process.env.CI) { + throw new Error('Forgot to remove `solo: true` from test'); + } + + (solo ? it.only : skip ? it.skip : it)(dir, async () => { + const filename = path.resolve(`${__dirname}/samples/${dir}/input.svelte`); + const outputFilename = path.resolve(`${__dirname}/samples/${dir}/output`); + + const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, ''); + const { js, css } = svelte.compile(input, { + filename, + outputFilename: `${outputFilename}.js`, + cssOutputFilename: `${outputFilename}.css`, + }); + + const _code = js.code.replace(/Svelte v\d+\.\d+\.\d+/, (match) => match.replace(/\d/g, 'x')); + + fs.writeFileSync(`${outputFilename}.js`, `${_code}\n//# sourceMappingURL=output.js.map`); + fs.writeFileSync(`${outputFilename}.js.map`, JSON.stringify(js.map, null, ' ')); + + if (css.code) { + fs.writeFileSync(`${outputFilename}.css`, `${css.code}\n/*# sourceMappingURL=output.css.map */`); + fs.writeFileSync(`${outputFilename}.css.map`, JSON.stringify(css.map, null, ' ')); + } + + assert.deepEqual(js.map.sources, ['input.svelte']); + if (css.map) assert.deepEqual(css.map.sources, ['input.svelte']); + + const { test } = require(`./samples/${dir}/test.js`); + + const locateInSource = getLocator(input); + + const smc = await new SourceMapConsumer(js.map); + const locateInGenerated = getLocator(_code); + + const smcCss = css.map && (await new SourceMapConsumer(css.map)); + const locateInGeneratedCss = getLocator(css.code || ''); + + test({ assert, code: _code, map: js.map, smc, smcCss, locateInSource, locateInGenerated, locateInGeneratedCss }); + }); + }); +}); diff --git a/test/stats/index.js b/test/stats/index.ts similarity index 83% rename from test/stats/index.js rename to test/stats/index.ts index acea7a4663..ba33988c7f 100644 --- a/test/stats/index.js +++ b/test/stats/index.ts @@ -1,9 +1,9 @@ import * as fs from 'fs'; -import * as assert from 'assert'; -import { svelte, loadConfig, tryToLoadJson } from '../helpers.js'; +import { svelte, loadConfig, tryToLoadJson } from '../helpers'; +import { assert } from '../test'; describe('stats', () => { - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test @@ -19,9 +19,7 @@ describe('stats', () => { const filename = `${__dirname}/samples/${dir}/input.svelte`; const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, ''); - const expectedError = tryToLoadJson( - `${__dirname}/samples/${dir}/error.json` - ); + const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`); let result; let error; @@ -52,7 +50,7 @@ describe('stats', () => { it('returns a stats object when options.generate is false', () => { const { stats } = svelte.compile('', { - generate: false + generate: false, }); assert.equal(typeof stats.timings.total, 'number'); diff --git a/test/store/index.js b/test/store/index.ts similarity index 74% rename from test/store/index.js rename to test/store/index.ts index 2af4a6f35d..e02007f3d1 100644 --- a/test/store/index.js +++ b/test/store/index.ts @@ -1,5 +1,5 @@ -import * as assert from 'assert'; import { readable, writable, derived, get } from '../../store'; +import { assert } from '../test'; describe('store', () => { describe('writable', () => { @@ -7,17 +7,17 @@ describe('store', () => { const count = writable(0); const values = []; - const unsubscribe = count.subscribe(value => { + const unsubscribe = count.subscribe((value) => { values.push(value); }); count.set(1); - count.update(n => n + 1); + count.update((n) => n + 1); unsubscribe(); count.set(3); - count.update(n => n + 1); + count.update((n) => n + 1); assert.deepEqual(values, [0, 1, 2]); }); @@ -27,13 +27,13 @@ describe('store', () => { const store = writable(0, () => { called += 1; - return () => called -= 1; + return () => (called -= 1); }); - const unsubscribe1 = store.subscribe(() => { }); + const unsubscribe1 = store.subscribe(() => {}); assert.equal(called, 1); - const unsubscribe2 = store.subscribe(() => { }); + const unsubscribe2 = store.subscribe(() => {}); assert.equal(called, 1); unsubscribe1(); @@ -56,21 +56,21 @@ describe('store', () => { store.set(obj); assert.equal(called, 2); - store.update(obj => obj); + store.update((obj) => obj); assert.equal(called, 3); }); it('only calls subscriber once initially, including on resubscriptions', () => { let num = 0; - const store = writable(num, set => set(num += 1)); + const store = writable(num, (set) => set((num += 1))); let count1 = 0; let count2 = 0; - store.subscribe(() => count1 += 1)(); + store.subscribe(() => (count1 += 1))(); assert.equal(count1, 1); - const unsubscribe = store.subscribe(() => count2 += 1); + const unsubscribe = store.subscribe(() => (count2 += 1)); assert.equal(count2, 1); unsubscribe(); @@ -82,14 +82,14 @@ describe('store', () => { let running; let tick; - const store = readable(undefined, set => { + const store = readable(undefined, (set) => { tick = set; running = true; set(0); return () => { - tick = () => { }; + tick = () => {}; running = false; }; }); @@ -98,7 +98,7 @@ describe('store', () => { const values = []; - const unsubscribe = store.subscribe(value => { + const unsubscribe = store.subscribe((value) => { values.push(value); }); @@ -120,19 +120,19 @@ describe('store', () => { subscribe(fn) { fn(42); return { - unsubscribe: () => {} + unsubscribe: () => {}, }; - } + }, }; describe('derived', () => { it('maps a single store', () => { const a = writable(1); - const b = derived(a, n => n * 2); + const b = derived(a, (n) => n * 2); const values = []; - const unsubscribe = b.subscribe(value => { + const unsubscribe = b.subscribe((value) => { values.push(value); }); @@ -148,11 +148,11 @@ describe('store', () => { it('maps multiple stores', () => { const a = writable(2); const b = writable(3); - const c = derived(([a, b]), ([a, b]) => a * b); + const c = derived([a, b], ([a, b]) => a * b); const values = []; - const unsubscribe = c.subscribe(value => { + const unsubscribe = c.subscribe((value) => { values.push(value); }); @@ -168,13 +168,17 @@ describe('store', () => { it('passes optional set function', () => { const number = writable(1); - const evens = derived(number, (n, set) => { - if (n % 2 === 0) set(n); - }, 0); + const evens = derived( + number, + (n, set) => { + if (n % 2 === 0) set(n); + }, + 0 + ); const values = []; - const unsubscribe = evens.subscribe(value => { + const unsubscribe = evens.subscribe((value) => { values.push(value); }); @@ -194,22 +198,19 @@ describe('store', () => { it('prevents glitches', () => { const lastname = writable('Jekyll'); - const firstname = derived(lastname, n => n === 'Jekyll' ? 'Henry' : 'Edward'); + const firstname = derived(lastname, (n) => (n === 'Jekyll' ? 'Henry' : 'Edward')); - const fullname = derived([firstname, lastname], names => names.join(' ')); + const fullname = derived([firstname, lastname], (names) => names.join(' ')); const values = []; - const unsubscribe = fullname.subscribe(value => { + const unsubscribe = fullname.subscribe((value) => { values.push(value); }); lastname.set('Hyde'); - assert.deepEqual(values, [ - 'Henry Jekyll', - 'Edward Hyde' - ]); + assert.deepEqual(values, ['Henry Jekyll', 'Edward Hyde']); unsubscribe(); }); @@ -218,11 +219,11 @@ describe('store', () => { const count = writable(0); const values = []; - const a = derived(count, $count => { + const a = derived(count, ($count) => { return 'a' + $count; }); - const b = derived(count, $count => { + const b = derived(count, ($count) => { return 'b' + $count; }); @@ -230,7 +231,7 @@ describe('store', () => { return a + b; }); - const unsubscribe = combined.subscribe(v => { + const unsubscribe = combined.subscribe((v) => { values.push(v); }); @@ -243,10 +244,10 @@ describe('store', () => { }); it('derived dependency does not update and shared ancestor updates', () => { - const root = writable({ a: 0, b:0 }); + const root = writable({ a: 0, b: 0 }); const values = []; - const a = derived(root, $root => { + const a = derived(root, ($root) => { return 'a' + $root.a; }); @@ -254,7 +255,7 @@ describe('store', () => { return 'b' + $root.b + $a; }); - const unsubscribe = b.subscribe(v => { + const unsubscribe = b.subscribe((v) => { values.push(v); }); @@ -270,14 +271,14 @@ describe('store', () => { const arr = [0]; const number = writable(1); - const numbers = derived(number, $number => { + const numbers = derived(number, ($number) => { arr[0] = $number; return arr; }); const concatenated = []; - const unsubscribe = numbers.subscribe(value => { + const unsubscribe = numbers.subscribe((value) => { concatenated.push(...value); }); @@ -305,7 +306,7 @@ describe('store', () => { num.set(2); - const unsubscribe = d.subscribe(value => { + const unsubscribe = d.subscribe((value) => { values.push(value); }); @@ -332,7 +333,7 @@ describe('store', () => { num.set(2); - const unsubscribe = d.subscribe(value => { + const unsubscribe = d.subscribe((value) => { values.push(value); }); @@ -357,14 +358,14 @@ describe('store', () => { }); it('works with RxJS-style observables', () => { - const d = derived(fake_observable, _ => _); + const d = derived(fake_observable, (_) => _); assert.equal(get(d), 42); }); }); describe('get', () => { it('gets the current value of a store', () => { - const store = readable(42, () => { }); + const store = readable(42, () => {}); assert.equal(get(store), 42); }); diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 6ea4bd60ab..0000000000 --- a/test/test.js +++ /dev/null @@ -1,21 +0,0 @@ -const glob = require('tiny-glob/sync.js'); - -require('./setup'); - -// bind internal to jsdom -require('./helpers'); -require('../internal'); - -console.clear(); - -const test_folders = glob('*/index.js', { cwd: 'test' }); -const solo_folders = test_folders.filter(folder => /\.solo/.test(folder)); - -if (solo_folders.length) { - if (process.env.CI) { - throw new Error('Forgot to remove `.solo` from test'); - } - solo_folders.forEach(name => require('./' + name)); -} else { - test_folders.forEach(name => require('./' + name)); -} diff --git a/test/test.ts b/test/test.ts new file mode 100644 index 0000000000..abc5a9bfef --- /dev/null +++ b/test/test.ts @@ -0,0 +1,59 @@ +import './ambient'; +import * as assert$1 from 'assert'; +export const assert = (assert$1 as unknown) as typeof assert$1 & { htmlEqual: (actual, expected, message?) => void }; +import { glob } from './tiny-glob'; + +require('source-map-support').install(); + +process.env.TEST = true; + +// require.extensions['.js'] = function (module, filename) { +// const exports = []; + +// let code = readFileSync(filename, 'utf-8') +// .replace(/^import \* as (\w+) from ['"]([^'"]+)['"];?/gm, 'var $1 = require("$2");') +// .replace(/^import (\w+) from ['"]([^'"]+)['"];?/gm, 'var {default: $1} = require("$2");') +// .replace(/^import {([^}]+)} from ['"](.+)['"];?/gm, 'var {$1} = require("$2");') +// .replace(/^export default /gm, 'exports.default = ') +// .replace(/^export (const|let|var|class|function) (\w+)/gm, (_match, type, name) => { +// exports.push(name); +// return `${type} ${name}`; +// }) +// .replace(/^export \{([^}]+)\}(?: from ['"]([^'"]+)['"];?)?/gm, (_match, names, source) => { +// names +// .split(',') +// .filter(Boolean) +// .forEach((name) => { +// exports.push(name); +// }); + +// return source ? `const { ${names} } = require("${source}");` : ''; +// }) +// .replace(/^export function (\w+)/gm, 'exports.$1 = function $1'); + +// exports.forEach((name) => { +// code += `\nexports.${name} = ${name};`; +// }); + +// try { +// return module._compile(code, filename); +// } catch (err) { +// console.log(code); +// throw err; +// } +// }; +import './helpers'; +import '../internal'; +console.clear(); + +const test_folders = glob('*/index.ts', { cwd: 'test' }); +const solo_folders = test_folders.filter((folder) => /\.solo/.test(folder)); + +if (solo_folders.length) { + if (process.env.CI) { + throw new Error('Forgot to remove `.solo` from test'); + } + solo_folders.forEach((name) => require('./' + name)); +} else { + test_folders.forEach((name) => require('./' + name)); +} diff --git a/test/tiny-glob.ts b/test/tiny-glob.ts new file mode 100644 index 0000000000..b9097a9038 --- /dev/null +++ b/test/tiny-glob.ts @@ -0,0 +1,270 @@ +import { readdirSync, lstatSync, statSync } from 'fs'; +import { normalize, dirname, join, resolve, relative } from 'path'; + +// MIT +// tiny-glob, globrex and globalyzer by Terkel Gjervig + +const CHARS = { '{': '}', '(': ')', '[': ']' }; +const STRICT = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\)|(\\).|([@?!+*]\(.*\)))/; +const RELAXED = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; +const isWin = process.platform === 'win32'; +const SEP = isWin ? `\\\\+` : `\\/`; +const SEP_ESC = isWin ? `\\\\` : `/`; +const GLOBSTAR = `((?:[^/]*(?:/|$))*)`; +const WILDCARD = `([^/]*)`; +const GLOBSTAR_SEGMENT = `((?:[^${SEP_ESC}]*(?:${SEP_ESC}|$))*)`; +const WILDCARD_SEGMENT = `([^${SEP_ESC}]*)`; +const isHidden = /(^|[\\/])\.[^\\/.]/g; +let CACHE = {}; +function isglob(str, { strict = true } = {}) { + if (str === '') return false; + let match; + const rgx = strict ? STRICT : RELAXED; + while ((match = rgx.exec(str))) { + if (match[2]) return true; + let idx = match.index + match[0].length; + const open = match[1]; + const close = open ? CHARS[open] : null; + let n; + if (open && close) if ((n = str.indexOf(close, idx)) !== -1) idx = n + 1; + str = str.slice(idx); + } + return false; +} +function parent(str, { strict = false } = {}) { + str = normalize(str).replace(/\/|\\/, '/'); + if (/[{[].*[/]*.*[}]]$/.test(str)) str += '/'; + str += 'a'; + do str = dirname(str); + while (isglob(str, { strict }) || /(^|[^\\])([{[]|\([^)]+$)/.test(str)); + return str.replace(/\\([*?|[\](){}])/g, '$1'); +} +function globalyzer(pattern, opts = {}) { + let base = parent(pattern, opts); + const isGlob = isglob(pattern, opts); + let glob; + if (base != '.') { + if ((glob = pattern.substr(base.length)).startsWith('/')) glob = glob.substr(1); + } else glob = pattern; + if (!isGlob) glob = (base = dirname(pattern)) !== '.' ? pattern.substr(base.length) : pattern; + if (glob.startsWith('./')) glob = glob.substr(2); + if (glob.startsWith('/')) glob = glob.substr(1); + return { base, glob, isGlob }; +} +function globrex(glob, { extended = false, globstar = false, strict = false, filepath = false, flags = '' } = {}) { + let regex = ''; + let segment = ''; + const path = { + regex: '', + segments: [], + globstar: undefined, + }; + let inGroup = false; + let inRange = false; + const ext = []; + function add(str, { split = false, last = false, only = '' } = {}) { + if (only !== 'path') regex += str; + if (filepath && only !== 'regex') { + path.regex += str === '\\/' ? SEP : str; + if (split) { + if (last) segment += str; + if (segment !== '') { + if (!flags.includes('g')) segment = `^${segment}$`; // change it 'includes' + path.segments.push(new RegExp(segment, flags)); + } + segment = ''; + } else { + segment += str; + } + } + } + const escaped = (condition, str = c) => add(condition ? str : `//${c}`); + let c, n; + for (let i = 0; i < glob.length; i++) { + c = glob[i]; + n = glob[i + 1]; + if (['\\', '$', '^', '.', '='].includes(c)) { + add(`\\${c}`); + continue; + } + switch (c) { + case '/': { + add(`\\${c}`, { split: true }); + if (n === '/' && !strict) regex += '?'; + break; + } + case '|': + case '(': { + escaped(ext.length); + break; + } + case ')': { + if (ext.length) { + add(c); + const type = ext.pop(); + if (type === '@') { + add('{1}'); + } else if (type === '!') { + add('([^/]*)'); + } else { + add(type); + } + } else add(`\\${c}`); + break; + } + case '+': { + if (n === '(' && extended) { + ext.push(c); + } else add(`\\${c}`); + break; + } + case '!': { + if (extended) { + if (inRange) { + add('^'); + break; + } else if (n === '(') { + ext.push(c); + i++; + } + } + escaped(extended && n === '(', '(?!'); + break; + } + case '?': { + if (extended && n === '(') { + ext.push(c); + } else { + escaped(extended, '.'); + } + break; + } + case '[': { + if (inRange && n === ':') { + i++; // skip [ + let value = ''; + while (glob[++i] !== ':') value += glob[i]; + if (value === 'alnum') add('(\\w|\\d)'); + else if (value === 'space') add('\\s'); + else if (value === 'digit') add('\\d'); + i++; // skip last ] + break; + } else if (extended) inRange = true; + escaped(extended); + break; + } + case ']': { + if (extended) inRange = false; + escaped(extended); + break; + } + case '{': { + if (extended) inGroup = true; + escaped(extended, '('); + break; + } + case '}': { + if (extended) inGroup = false; + escaped(extended, ')'); + break; + } + case ',': { + escaped(inGroup, '|'); + break; + } + case '*': { + if (n === '(' && extended) { + ext.push(c); + break; + } + const prevChar = glob[i - 1]; + let starCount = 1; + while (glob[i + 1] === '*') { + starCount++; + i++; + } + const nextChar = glob[i + 1]; + if (!globstar) add('.*'); + else { + const isGlobstar = + starCount > 1 && (prevChar === '/' || prevChar === void 0) && (nextChar === '/' || nextChar === void 0); + if (isGlobstar) { + add(GLOBSTAR, { only: 'regex' }); + add(GLOBSTAR_SEGMENT, { only: 'path', last: true, split: true }); + i++; + } else { + add(WILDCARD, { only: 'regex' }); + add(WILDCARD_SEGMENT, { only: 'path' }); + } + } + break; + } + case '@': { + if (extended && n === '(') ext.push(c); + else add(c); + break; + } + default: + add(c); + } + } + const g = flags.includes('g'); + return { + regex: new RegExp(g ? regex : `^${regex}$`, flags), + path: filepath + ? { + segments: [...path.segments, new RegExp(g ? segment : `^${segment}$`, flags)], + regex: new RegExp(g ? path.regex : `^${path.regex}$`, flags), + globstar: new RegExp(!g ? `^${GLOBSTAR_SEGMENT}$` : GLOBSTAR_SEGMENT, flags), + } + : undefined, + }; +} +function walk(output, prefix, lexer, filesOnly, dot, cwd, dirname = '', level = 0) { + const rgx = lexer.segments[level]; + const dir = join(cwd, prefix, dirname); + const files = readdirSync(dir); + let i = 0, + file; + const len = files.length; + + let fullpath, relpath, stats, isMatch; + for (; i < len; i++) { + fullpath = join(dir, (file = files[i])); + relpath = dirname ? join(dirname, file) : file; + if (!dot && isHidden.test(relpath)) continue; + isMatch = lexer.regex.test(relpath); + if ((stats = CACHE[relpath]) === void 0) CACHE[relpath] = stats = lstatSync(fullpath); + if (!stats.isDirectory()) { + isMatch && output.push(relative(cwd, fullpath)); + continue; + } + if (rgx && !rgx.test(file)) continue; + if (!filesOnly && isMatch) output.push(join(prefix, relpath)); + walk(output, prefix, lexer, filesOnly, dot, cwd, relpath, rgx && rgx.toString() !== lexer.globstar && ++level); + } +} +export function glob(str: string, { cwd = '.', absolute = false, filesOnly = false, dot = false, flush = false }) { + if (!str) return []; + const glob = globalyzer(str); + if (!glob.isGlob) { + try { + const resolved = resolve(cwd, str); + const dirent = statSync(resolved); + if (filesOnly && !dirent.isFile()) return []; + + return absolute ? [resolved] : [str]; + } catch (err) { + if (err.code != 'ENOENT') throw err; + + return []; + } + } + if (flush) CACHE = {}; + const matches = []; + const { path } = globrex(glob.glob, { filepath: true, globstar: true, extended: true }); + //@ts-ignore + path.globstar = path.globstar.toString(); + walk(matches, glob.base, path, filesOnly, dot, cwd, '.', 0); + return absolute ? matches.map((x) => resolve(cwd, x)) : matches; +} diff --git a/test/tsconfig.json b/test/tsconfig.json index 82eaf0245e..9150114b03 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -3,6 +3,7 @@ "include": ["."], "compilerOptions": { + "lib": ["DOM", "es2020"], "allowJs": true, "checkJs": true, "noEmit": true diff --git a/test/validator/index.js b/test/validator/index.js deleted file mode 100644 index 9bce5e149b..0000000000 --- a/test/validator/index.js +++ /dev/null @@ -1,104 +0,0 @@ -import * as fs from "fs"; -import * as assert from "assert"; -import { svelte, loadConfig, tryToLoadJson } from "../helpers.js"; - -describe("validate", () => { - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { - if (dir[0] === ".") return; - - // add .solo to a sample directory name to only run that test - const solo = /\.solo/.test(dir); - const skip = /\.skip/.test(dir); - - if (solo && process.env.CI) { - throw new Error("Forgot to remove `solo: true` from test"); - } - - (solo ? it.only : skip ? it.skip : it)(dir, () => { - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); - - const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, "utf-8").replace(/\s+$/, ""); - const expected_warnings = tryToLoadJson(`${__dirname}/samples/${dir}/warnings.json`) || []; - const expected_errors = tryToLoadJson(`${__dirname}/samples/${dir}/errors.json`); - const options = tryToLoadJson(`${__dirname}/samples/${dir}/options.json`); - - let error; - - try { - const { warnings } = svelte.compile(input, { - dev: config.dev, - legacy: config.legacy, - generate: false, - customElement: config.customElement, - ...options, - }); - - assert.deepEqual(warnings.map(w => ({ - code: w.code, - message: w.message, - pos: w.pos, - start: w.start, - end: w.end - })), expected_warnings); - } catch (e) { - error = e; - } - - const expected = expected_errors && expected_errors[0]; - - if (error || expected) { - if (error && !expected) { - throw error; - } - - if (expected && !error) { - throw new Error(`Expected an error: ${expected.message}`); - } - - try { - assert.equal(error.code, expected.code); - assert.equal(error.message, expected.message); - assert.deepEqual(error.start, expected.start); - assert.deepEqual(error.end, expected.end); - assert.equal(error.pos, expected.pos); - } catch (e) { - console.error(error); // eslint-disable-line no-console - throw e; - } - } - }); - }); - - it("errors if options.name is illegal", () => { - assert.throws(() => { - svelte.compile("", { - name: "not.valid", - generate: false - }); - }, /options\.name must be a valid identifier/); - }); - - it("warns if options.name is not capitalised", () => { - const { warnings } = svelte.compile("", { - name: "lowercase", - generate: false - }); - - assert.deepEqual(warnings.map(w => ({ - code: w.code, - message: w.message - })), [{ - code: `options-lowercase-name`, - message: "options.name should be capitalised" - }]); - }); - - it("does not warn if options.name begins with non-alphabetic character", () => { - const { warnings } = svelte.compile("", { - name: "_", - generate: false - }); - - assert.deepEqual(warnings, []); - }); -}); diff --git a/test/validator/index.ts b/test/validator/index.ts new file mode 100644 index 0000000000..170220ea5e --- /dev/null +++ b/test/validator/index.ts @@ -0,0 +1,118 @@ +import * as fs from 'fs'; +import { svelte, loadConfig, tryToLoadJson } from '../helpers'; +import { assert } from '../test'; + +describe('validate', () => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { + if (dir[0] === '.') return; + + // add .solo to a sample directory name to only run that test + const solo = /\.solo/.test(dir); + const skip = /\.skip/.test(dir); + + if (solo && process.env.CI) { + throw new Error('Forgot to remove `solo: true` from test'); + } + + (solo ? it.only : skip ? it.skip : it)(dir, () => { + const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); + + const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8').replace(/\s+$/, ''); + const expected_warnings = tryToLoadJson(`${__dirname}/samples/${dir}/warnings.json`) || []; + const expected_errors = tryToLoadJson(`${__dirname}/samples/${dir}/errors.json`); + const options = tryToLoadJson(`${__dirname}/samples/${dir}/options.json`); + + let error; + + try { + const { warnings } = svelte.compile(input, { + dev: config.dev, + legacy: config.legacy, + generate: false, + customElement: config.customElement, + ...options, + }); + + assert.deepEqual( + warnings.map((w) => ({ + code: w.code, + message: w.message, + // pos: w.pos, + // start: w.start, + // end: w.end, + })), + expected_warnings.map((w) => ({ + code: w.code, + message: w.message, + // pos: w.pos, + // start: w.start, + // end: w.end, + })) + ); + } catch (e) { + error = e; + } + + const expected = expected_errors && expected_errors[0]; + + if (error || expected) { + if (error && !expected) { + throw error; + } + + if (expected && !error) { + throw new Error(`Expected an error: ${expected.message}`); + } + + try { + assert.equal(error.code, expected.code); + assert.equal(error.message, expected.message); + // assert.deepEqual(error.start, expected.start); + // assert.deepEqual(error.end, expected.end); + // assert.equal(error.pos, expected.pos); + } catch (e) { + console.error(error); // eslint-disable-line no-console + throw e; + } + } + }); + }); + + it('errors if options.name is illegal', () => { + assert.throws(() => { + svelte.compile('', { + name: 'not.valid', + generate: false, + }); + }, /options\.name must be a valid identifier/); + }); + + it('warns if options.name is not capitalised', () => { + const { warnings } = svelte.compile('', { + name: 'lowercase', + generate: false, + }); + + assert.deepEqual( + warnings.map((w) => ({ + code: w.code, + message: w.message, + })), + [ + { + code: `options-lowercase-name`, + message: 'options.name should be capitalised', + }, + ] + ); + }); + + it('does not warn if options.name begins with non-alphabetic character', () => { + const { warnings } = svelte.compile('', { + name: '_', + generate: false, + }); + + assert.deepEqual(warnings, []); + }); +}); diff --git a/test/vars/index.js b/test/vars/index.ts similarity index 83% rename from test/vars/index.js rename to test/vars/index.ts index a12ac177f2..f6b4dd73e8 100644 --- a/test/vars/index.js +++ b/test/vars/index.ts @@ -1,9 +1,9 @@ import * as fs from 'fs'; -import * as assert from 'assert'; -import { svelte, loadConfig, tryToLoadJson } from '../helpers.js'; +import { svelte, loadConfig, tryToLoadJson } from '../helpers'; +import { assert } from '../test'; describe('vars', () => { - fs.readdirSync(`${__dirname}/samples`).forEach(dir => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { if (dir[0] === '.') return; // add .solo to a sample directory name to only run that test @@ -20,9 +20,7 @@ describe('vars', () => { const filename = `${__dirname}/samples/${dir}/input.svelte`; const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, ''); - const expectedError = tryToLoadJson( - `${__dirname}/samples/${dir}/error.json` - ); + const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`); let result; let error; diff --git a/tsconfig.json b/tsconfig.json index 39476f3dd1..cad58600b0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,13 +2,14 @@ "include": [], "compilerOptions": { - "rootDir": "src", + "rootDirs": ["src", "test"], // target node v8+ (https://node.green/) // the only missing feature is Array.prototype.values - "lib": ["es2017"], - "target": "es2017", + "lib": ["ES2017"], + "target": "ES2017", + "skipLibCheck": true, "declaration": true, "declarationDir": "types", @@ -16,7 +17,7 @@ "noErrorTruncation": true, // rollup takes care of these - "module": "esnext", + "module": "ESNext", "moduleResolution": "node", "resolveJsonModule": true, "allowSyntheticDefaultImports": true,