Merge branch 'master' into pr/5608

pull/5608/head
Conduitry 5 years ago
commit 4ad5d42624

4
.gitignore vendored

@ -18,10 +18,6 @@ node_modules
/coverage/
/coverage.lcov
/test/*/samples/_
/test/sourcemaps/samples/*/output.js
/test/sourcemaps/samples/*/output.js.map
/test/sourcemaps/samples/*/output.css
/test/sourcemaps/samples/*/output.css.map
/yarn-error.log
_actual*.*
_output

@ -1,5 +1,9 @@
# Svelte changelog
## Unreleased
* Add `Element` and `Node` to known globals ([#5586](https://github.com/sveltejs/svelte/issues/5586))
## 3.29.4
* Fix code generation error with `??` alongside logical operators ([#5558](https://github.com/sveltejs/svelte/issues/5558))

4
package-lock.json generated

@ -144,8 +144,8 @@
}
},
"@sveltejs/eslint-config": {
"version": "github:sveltejs/eslint-config#5d1ba28f99568e42f26d9b7484ab57720f6ec9b4",
"from": "github:sveltejs/eslint-config#v5.4.0",
"version": "github:sveltejs/eslint-config#54081d752d199dba97db9f578665c87f18469da3",
"from": "github:sveltejs/eslint-config#v5.5.0",
"dev": true
},
"@tootallnate/once": {

@ -37,7 +37,7 @@
"posttest": "agadoo internal/index.mjs",
"prepublishOnly": "npm run lint && PUBLISH=true npm test",
"tsd": "tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly",
"lint": "eslint '{src,test}/**/*.{ts,js}'"
"lint": "eslint \"{src,test}/**/*.{ts,js}\""
},
"repository": {
"type": "git",
@ -63,7 +63,7 @@
"@rollup/plugin-sucrase": "^3.0.0",
"@rollup/plugin-typescript": "^2.0.1",
"@rollup/plugin-virtual": "^2.0.0",
"@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.4.0",
"@sveltejs/eslint-config": "github:sveltejs/eslint-config#v5.5.0",
"@types/mocha": "^7.0.0",
"@types/node": "^8.10.53",
"@typescript-eslint/eslint-plugin": "^3.0.2",

@ -115,8 +115,9 @@
on:mousedown={handleMousedown}
bind:currentTime={time}
bind:duration
bind:paused
></video>
bind:paused>
<track kind="captions"/>
</video>
<div class="controls" style="opacity: {duration && showControls ? 1 : 0}">
<progress value="{(time / duration) || 0}"/>

@ -521,8 +521,7 @@ export default class Component {
if (this.hoistable_nodes.has(node)) return false;
if (this.reactive_declaration_nodes.has(node)) return false;
if (node.type === 'ImportDeclaration') return false;
if (node.type === 'ExportDeclaration' && node.specifiers.length > 0)
return false;
if (node.type === 'ExportDeclaration' && node.specifiers.length > 0) return false;
return true;
});
}
@ -1038,8 +1037,9 @@ export default class Component {
this.vars.find(
variable => variable.name === name && variable.module
)
)
) {
return false;
}
return true;
});
@ -1288,8 +1288,9 @@ export default class Component {
declaration.dependencies.forEach(name => {
if (declaration.assignees.has(name)) return;
const earlier_declarations = lookup.get(name);
if (earlier_declarations)
if (earlier_declarations) {
earlier_declarations.forEach(add_declaration);
}
});
this.reactive_declarations.push(declaration);
@ -1319,8 +1320,9 @@ export default class Component {
if (globals.has(name) && node.type !== 'InlineComponent') return;
let message = `'${name}' is not defined`;
if (!this.ast.instance)
if (!this.ast.instance) {
message += `. Consider adding a <script> block with 'export let ${name}' to declare a prop`;
}
this.warn(node, {
code: 'missing-declaration',
@ -1382,8 +1384,9 @@ function process_component_options(component: Component, nodes) {
const message = "'tag' must be a string literal";
const tag = get_value(attribute, code, message);
if (typeof tag !== 'string' && tag !== null)
if (typeof tag !== 'string' && tag !== null) {
component.error(attribute, { code, message });
}
if (tag && !/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z0-9-]+$/.test(tag)) {
component.error(attribute, {
@ -1408,8 +1411,9 @@ function process_component_options(component: Component, nodes) {
const message = "The 'namespace' attribute must be a string literal representing a valid namespace";
const ns = get_value(attribute, code, message);
if (typeof ns !== 'string')
if (typeof ns !== 'string') {
component.error(attribute, { code, message });
}
if (valid_namespaces.indexOf(ns) === -1) {
const match = fuzzymatch(ns, valid_namespaces);
@ -1437,8 +1441,9 @@ function process_component_options(component: Component, nodes) {
const message = `${name} attribute must be true or false`;
const value = get_value(attribute, code, message);
if (typeof value !== 'boolean')
if (typeof value !== 'boolean') {
component.error(attribute, { code, message });
}
component_options[name] = value;
break;

@ -67,17 +67,21 @@ export default class Binding extends Node {
} else {
const variable = component.var_lookup.get(name);
if (!variable || variable.global) component.error(this.expression.node, {
code: 'binding-undeclared',
message: `${name} is not declared`
});
if (!variable || variable.global) {
component.error(this.expression.node, {
code: 'binding-undeclared',
message: `${name} is not declared`
});
}
variable[this.expression.node.type === 'MemberExpression' ? 'mutated' : 'reassigned'] = true;
if (info.expression.type === 'Identifier' && !variable.writable) component.error(this.expression.node, {
code: 'invalid-binding',
message: 'Cannot bind to a variable which is not writable'
});
if (info.expression.type === 'Identifier' && !variable.writable) {
component.error(this.expression.node, {
code: 'invalid-binding',
message: 'Cannot bind to a variable which is not writable'
});
}
}
const type = parent.get_static_attribute_value('type');

@ -48,9 +48,10 @@ export default class AttributeWrapper extends BaseAttributeWrapper {
// special case — <option value={foo}> — see below
if (this.parent.node.name === 'option' && node.name === 'value') {
let select: ElementWrapper = this.parent;
while (select && (select.node.type !== 'Element' || select.node.name !== 'select'))
while (select && (select.node.type !== 'Element' || select.node.name !== 'select')) {
// @ts-ignore todo: doublecheck this, but looks to be correct
select = select.parent;
}
if (select && select.select_binding_dependencies) {
select.select_binding_dependencies.forEach(prop => {

@ -178,11 +178,12 @@ export class Parser {
}
read_until(pattern: RegExp) {
if (this.index >= this.template.length)
if (this.index >= this.template.length) {
this.error({
code: 'unexpected-eof',
message: 'Unexpected end of input'
});
}
const start = this.index;
const match = pattern.exec(this.template.slice(start));

@ -32,10 +32,12 @@ export default function read_script(parser: Parser, start: number, attributes: N
const script_start = parser.index;
const script_end = parser.template.indexOf(script_closing_tag, script_start);
if (script_end === -1) parser.error({
code: 'unclosed-script',
message: '<script> must have a closing tag'
});
if (script_end === -1) {
parser.error({
code: 'unclosed-script',
message: '<script> must have a closing tag'
});
}
const source = parser.template.slice(0, script_start).replace(/[^\n]/g, ' ') +
parser.template.slice(script_start, script_end);

@ -288,10 +288,12 @@ export default function mustache(parser: Parser) {
if (parser.eat(',')) {
parser.allow_whitespace();
block.index = parser.read_identifier();
if (!block.index) parser.error({
code: 'expected-name',
message: 'Expected name'
});
if (!block.index) {
parser.error({
code: 'expected-name',
message: 'Expected name'
});
}
parser.allow_whitespace();
}

@ -13,8 +13,9 @@ const GRAM_SIZE_UPPER = 3;
// return an edit distance from 0 to 1
function _distance(str1: string, str2: string) {
if (str1 === null && str2 === null)
if (str1 === null && str2 === null) {
throw 'Trying to compare two null values';
}
if (str1 === null || str2 === null) return 0;
str1 = String(str1);
str2 = String(str2);

@ -13,6 +13,7 @@ export const globals = new Set([
'decodeURI',
'decodeURIComponent',
'document',
'Element',
'encodeURI',
'encodeURIComponent',
'Error',
@ -36,6 +37,7 @@ export const globals = new Set([
'NaN',
'navigator',
'Number',
'Node',
'Object',
'parseFloat',
'parseInt',

@ -34,9 +34,10 @@ function tick_spring<T>(ctx: TickContext<T>, last_value: T, current_value: T, ta
tick_spring(ctx, last_value[i], current_value[i], target_value[i]));
} else if (typeof current_value === 'object') {
const next_value = {};
for (const k in current_value)
for (const k in current_value) {
// @ts-ignore
next_value[k] = tick_spring(ctx, last_value[k], current_value[k], target_value[k]);
}
// @ts-ignore
return next_value;
} else {
@ -121,8 +122,9 @@ export function spring<T=any>(value?: T, opts: SpringOpts = {}): Spring<T> {
last_value = value;
store.set(value = next_value);
if (ctx.settled)
if (ctx.settled) {
task = null;
}
return !ctx.settled;
});
}

@ -118,9 +118,11 @@ describe('hydration', () => {
throw err;
}
if (config.show) showOutput(cwd, {
hydratable: true
});
if (config.show) {
showOutput(cwd, {
hydratable: true
});
}
});
}

@ -8,16 +8,21 @@ describe('preprocess', () => {
const config = loadConfig(`${__dirname}/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');
}
(config.skip ? it.skip : solo ? it.only : it)(dir, async () => {
(skip ? it.skip : solo ? it.only : it)(dir, async () => {
const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8');
const expected = fs.readFileSync(`${__dirname}/samples/${dir}/output.svelte`, 'utf-8');
const result = await svelte.preprocess(input, config.preprocess);
const result = await svelte.preprocess(
input,
config.preprocess || {},
config.options || { filename: 'input.svelte' }
);
fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, result.code);
assert.equal(result.code, expected);

@ -1,6 +1,5 @@
export default {
preprocess: {
filename: 'file.svelte',
markup: ({ content, filename }) => {
return {
code: content.replace('__MARKUP_FILENAME__', filename)
@ -16,5 +15,8 @@ export default {
code: content.replace('__SCRIPT_FILENAME__', filename)
};
}
},
options: {
filename: 'file.svelte'
}
};

@ -1,73 +1,113 @@
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 { loadConfig, svelte } from '../helpers';
// keep source-map at version 0.7.x
// https://github.com/mozilla/source-map/issues/400
import { getLocator } from 'locate-character';
import { SourceMapConsumer } from 'source-map';
describe('sourcemaps', () => {
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
const solo = /\.solo/.test(dir);
const skip = /\.skip/.test(dir);
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');
}
(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 { test } = require(`./samples/${dir}/test.js`);
const inputFile = path.resolve(`${__dirname}/samples/${dir}/input.svelte`);
const outputName = '_actual';
const outputBase = path.resolve(`${__dirname}/samples/${dir}/${outputName}`);
const inputCode = fs.readFileSync(inputFile, 'utf-8');
const input = {
code: inputCode,
locate: getLocator(inputCode)
};
const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, '');
const { js, css } = svelte.compile(input, {
filename,
outputFilename: `${outputFilename}.js`,
cssOutputFilename: `${outputFilename}.css`
const preprocessed = await svelte.preprocess(
input.code,
config.preprocess || {},
{
filename: 'input.svelte'
}
);
const { js, css } = svelte.compile(
preprocessed.code, {
filename: 'input.svelte',
// filenames for sourcemaps
outputFilename: `${outputName}.js`,
cssOutputFilename: `${outputName}.css`
});
const _code = js.code.replace(/Svelte v\d+\.\d+\.\d+/, match => match.replace(/\d/g, 'x'));
js.code = js.code.replace(
/generated by Svelte v\d+\.\d+\.\d+/,
match => match.replace(/\d/g, 'x')
);
fs.writeFileSync(`${outputBase}.svelte`, preprocessed.code);
if (preprocessed.map) {
fs.writeFileSync(
`${outputBase}.svelte.map`,
// TODO encode mappings for output - svelte.preprocess returns decoded mappings
JSON.stringify(preprocessed.map, null, 2)
);
}
fs.writeFileSync(
`${outputFilename}.js`,
`${_code}\n//# sourceMappingURL=output.js.map`
`${outputBase}.js`,
`${js.code}\n//# sourceMappingURL=${outputName}.js.map`
);
fs.writeFileSync(
`${outputFilename}.js.map`,
JSON.stringify(js.map, null, ' ')
`${outputBase}.js.map`,
JSON.stringify(js.map, null, 2)
);
if (css.code) {
fs.writeFileSync(
`${outputFilename}.css`,
`${css.code}\n/*# sourceMappingURL=output.css.map */`
`${outputBase}.css`,
`${css.code}\n/*# sourceMappingURL=${outputName}.css.map */`
);
fs.writeFileSync(
`${outputFilename}.css.map`,
`${outputBase}.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`);
assert.deepEqual(
js.map.sources.slice().sort(),
(config.js_map_sources || ['input.svelte']).sort()
);
if (css.map) {
assert.deepEqual(
css.map.sources.slice().sort(),
(config.css_map_sources || ['input.svelte']).sort()
);
}
const locateInSource = getLocator(input);
// use locate_1 with mapConsumer:
// lines are one-based, columns are zero-based
const smc = await new SourceMapConsumer(js.map);
const locateInGenerated = getLocator(_code);
preprocessed.mapConsumer = preprocessed.map && await new SourceMapConsumer(preprocessed.map);
preprocessed.locate = getLocator(preprocessed.code);
preprocessed.locate_1 = getLocator(preprocessed.code, { offsetLine: 1 });
const smcCss = css.map && await new SourceMapConsumer(css.map);
const locateInGeneratedCss = getLocator(css.code || '');
js.mapConsumer = js.map && await new SourceMapConsumer(js.map);
js.locate = getLocator(js.code);
js.locate_1 = getLocator(js.code, { offsetLine: 1 });
test({ assert, code: _code, map: js.map, smc, smcCss, locateInSource, locateInGenerated, locateInGeneratedCss });
css.mapConsumer = css.map && await new SourceMapConsumer(css.map);
css.locate = getLocator(css.code || '');
css.locate_1 = getLocator(css.code || '', { offsetLine: 1 });
test({ assert, input, preprocessed, js, css });
});
});
});

@ -1,12 +1,12 @@
export function test({ assert, smc, locateInSource, locateInGenerated }) {
const expected = locateInSource('foo.bar.baz');
export function test({ assert, input, js }) {
const expected = input.locate('foo.bar.baz');
let start;
let actual;
start = locateInGenerated('ctx[0].bar.baz');
start = js.locate('ctx[0].bar.baz');
actual = smc.originalPositionFor({
actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});
@ -18,9 +18,9 @@ export function test({ assert, smc, locateInSource, locateInGenerated }) {
column: expected.column
});
start = locateInGenerated('ctx[0].bar.baz', start.character + 1);
start = js.locate('ctx[0].bar.baz', start.character + 1);
actual = smc.originalPositionFor({
actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});

@ -1,13 +1,14 @@
export function test({ assert, smc, locateInSource, locateInGenerated }) {
const expected = locateInSource('potato');
export function test({ assert, input, js }) {
const expected = input.locate('potato');
let start;
start = locateInGenerated('potato');
start = locateInGenerated('potato', start.character + 1);
start = locateInGenerated('potato', start.character + 1); // we need the third instance of 'potato'
start = js.locate('potato');
start = js.locate('potato', start.character + 1);
start = js.locate('potato', start.character + 1);
// we need the third instance of 'potato'
const actual = smc.originalPositionFor({
const actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});

@ -1,12 +1,12 @@
export function test({ assert, smc, locateInSource, locateInGenerated }) {
const expected = locateInSource('bar.baz');
export function test({ assert, input, js }) {
const expected = input.locate('bar.baz');
let start;
let actual;
start = locateInGenerated('bar.baz');
start = js.locate('bar.baz');
actual = smc.originalPositionFor({
actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});
@ -18,9 +18,9 @@ export function test({ assert, smc, locateInSource, locateInGenerated }) {
column: expected.column
});
start = locateInGenerated('bar.baz', start.character + 1);
start = js.locate('bar.baz', start.character + 1);
actual = smc.originalPositionFor({
actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});

@ -1,14 +1,14 @@
export function test({ assert, smcCss, locateInSource, locateInGeneratedCss }) {
const expected = locateInSource( '.foo' );
export function test({ assert, input, css }) {
const expected = input.locate('.foo');
const start = locateInGeneratedCss( '.foo' );
const start = css.locate('.foo');
const actual = smcCss.originalPositionFor({
const actual = css.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});
assert.deepEqual( actual, {
assert.deepEqual(actual, {
source: 'input.svelte',
name: null,
line: expected.line + 1,

@ -1,10 +1,10 @@
export function test({ assert, code, smc, map, locateInSource, locateInGenerated }) {
const startIndex = code.indexOf('create_main_fragment');
export function test({ assert, input, js }) {
const startIndex = js.code.indexOf('create_main_fragment');
const expected = locateInSource('each');
const start = locateInGenerated('length', startIndex);
const expected = input.locate('each');
const start = js.locate('length', startIndex);
const actual = smc.originalPositionFor({
const actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});

@ -1,13 +1,13 @@
export function test({ assert, smc, locateInSource, locateInGenerated }) {
const expected = locateInSource( 'assertThisLine' );
const start = locateInGenerated( 'assertThisLine' );
export function test({ assert, input, js }) {
const expected = input.locate('assertThisLine');
const start = js.locate('assertThisLine');
const actual = smc.originalPositionFor({
const actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});
assert.deepEqual( actual, {
assert.deepEqual(actual, {
source: 'input.svelte',
name: null,
line: expected.line + 1,

@ -1,8 +1,8 @@
export function test({ assert, smc, locateInSource, locateInGenerated }) {
const expected = locateInSource( '42' );
const start = locateInGenerated( '42' );
export function test({ assert, input, js }) {
const expected = input.locate('42');
const start = js.locate('42');
const actual = smc.originalPositionFor({
const actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});

@ -1,9 +1,9 @@
const fs = require( 'fs' );
const path = require( 'path' );
const fs = require('fs');
const path = require('path');
export function test({ assert, map }) {
assert.deepEqual( map.sources, [ 'input.svelte' ]);
assert.deepEqual( map.sourcesContent, [
fs.readFileSync( path.join( __dirname, 'input.svelte' ), 'utf-8' )
export function test({ assert, js }) {
assert.deepEqual(js.map.sources, ['input.svelte']);
assert.deepEqual(js.map.sourcesContent, [
fs.readFileSync(path.join(__dirname, 'input.svelte'), 'utf-8')
]);
}

@ -1,8 +1,8 @@
export function test({ assert, smc, locateInSource, locateInGenerated }) {
const expected = locateInSource( 'assertThisLine' );
const start = locateInGenerated( 'assertThisLine' );
export function test({ assert, input, js }) {
const expected = input.locate( 'assertThisLine' );
const start = js.locate( 'assertThisLine' );
const actual = smc.originalPositionFor({
const actual = js.mapConsumer.originalPositionFor({
line: start.line + 1,
column: start.column
});

Loading…
Cancel
Save