pull/4886/head
pushkine 5 years ago
parent 25488772e2
commit a4f7624b9a

@ -1 +0,0 @@
test/test.js

@ -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": [

@ -65,7 +65,7 @@ export default [
}),
ts_plugin,
{
writeBundle(bundle) {
writeBundle(_options, bundle) {
if (dir === 'internal') {
const mod = bundle['index.mjs'];
if (mod) {

@ -1,5 +1,7 @@
{
"rules": {
"no-console": "off"
}
"rules": {
"no-console": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-namespace": "off"
}
}

@ -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;
}
}
}

@ -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,7 +146,7 @@ describe('css', () => {
function read(file) {
try {
return fs.readFileSync(file, 'utf-8');
return readFileSync(file, 'utf-8');
} catch (err) {
return null;
}

@ -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
);
}
}
});

@ -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('<main></main>', {virtualConsole}).window;
const { window } = new jsdom.JSDOM('<main></main>', { 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;
}
},
};
}

@ -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);
});
});

@ -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);

@ -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
);
});

@ -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')
);
});

@ -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);
});
});

@ -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);
}
});
});

@ -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
);
});

@ -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')
);
}
});

@ -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`);

@ -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 = '<div></div>') {
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/);
});

@ -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) {

@ -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;
}
};

@ -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 });
});
});
});

@ -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 });
});
});
});

@ -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');

@ -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);
});

@ -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));
}

@ -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));
}

@ -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;
}

@ -3,6 +3,7 @@
"include": ["."],
"compilerOptions": {
"lib": ["DOM", "es2020"],
"allowJs": true,
"checkJs": true,
"noEmit": true

@ -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("<div></div>", {
name: "not.valid",
generate: false
});
}, /options\.name must be a valid identifier/);
});
it("warns if options.name is not capitalised", () => {
const { warnings } = svelte.compile("<div></div>", {
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("<div></div>", {
name: "_",
generate: false
});
assert.deepEqual(warnings, []);
});
});

@ -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('<div></div>', {
name: 'not.valid',
generate: false,
});
}, /options\.name must be a valid identifier/);
});
it('warns if options.name is not capitalised', () => {
const { warnings } = svelte.compile('<div></div>', {
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('<div></div>', {
name: '_',
generate: false,
});
assert.deepEqual(warnings, []);
});
});

@ -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;

@ -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,

Loading…
Cancel
Save