reduce duplication by moving validation into stylesheet

pull/698/head
Rich Harris 8 years ago
parent 75fa924e89
commit 65e27c98a0

@ -50,6 +50,7 @@ export default class Generator {
parsed: Parsed, parsed: Parsed,
source: string, source: string,
name: string, name: string,
stylesheet: Stylesheet,
options: CompileOptions options: CompileOptions
) { ) {
this.ast = clone(parsed); this.ast = clone(parsed);
@ -76,7 +77,7 @@ export default class Generator {
this.usesRefs = false; this.usesRefs = false;
// styles // styles
this.stylesheet = new Stylesheet(source, parsed, options.filename, options.cascade !== false); this.stylesheet = stylesheet;
// TODO this is legacy — just to get the tests to pass during the transition // TODO this is legacy — just to get the tests to pass during the transition
this.css = this.stylesheet.render(options.cssOutputFilename).css; this.css = this.stylesheet.render(options.cssOutputFilename).css;

@ -1,5 +1,6 @@
import MagicString from 'magic-string'; import MagicString from 'magic-string';
import { groupSelectors, isGlobalSelector } from '../utils/css'; import { groupSelectors } from '../utils/css';
import { Validator } from '../validate/index';
import { Node } from '../interfaces'; import { Node } from '../interfaces';
export default class Selector { export default class Selector {
@ -72,6 +73,35 @@ export default class Selector {
} }
}); });
} }
validate(validator: Validator) {
this.blocks.forEach((block) => {
let i = block.selectors.length;
while (i-- > 1) {
const part = block.selectors[i];
if (part.type === 'PseudoClassSelector' && part.name === 'global') {
validator.error(`:global(...) must be the first element in a compound selector`, part.start);
}
}
});
let start = 0;
let end = this.blocks.length;
for (; start < end; start += 1) {
if (!this.blocks[start].global) break;
}
for (; end > start; end -= 1) {
if (!this.blocks[end - 1].global) break;
}
for (let i = start; i < end; i += 1) {
if (this.blocks[i].global) {
validator.error(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, this.blocks[i].selectors[0].start);
}
}
}
} }
function isDescendantSelector(selector: Node) { function isDescendantSelector(selector: Node) {

@ -3,6 +3,7 @@ import { walk } from 'estree-walker';
import { getLocator } from 'locate-character'; import { getLocator } from 'locate-character';
import Selector from './Selector'; import Selector from './Selector';
import getCodeFrame from '../utils/getCodeFrame'; import getCodeFrame from '../utils/getCodeFrame';
import { Validator } from '../validate/index';
import { Node, Parsed } from '../interfaces'; import { Node, Parsed } from '../interfaces';
class Rule { class Rule {
@ -193,6 +194,14 @@ export default class Stylesheet {
}; };
} }
validate(validator: Validator) {
this.rules.forEach(rule => {
rule.selectors.forEach(selector => {
selector.validate(validator);
});
});
}
warnOnUnusedSelectors(onwarn) { warnOnUnusedSelectors(onwarn) {
if (this.cascade) return; if (this.cascade) return;

@ -9,6 +9,7 @@ import CodeBuilder from '../../utils/CodeBuilder';
import visit from './visit'; import visit from './visit';
import shared from './shared'; import shared from './shared';
import Generator from '../Generator'; import Generator from '../Generator';
import Stylesheet from '../Stylesheet';
import preprocess from './preprocess'; import preprocess from './preprocess';
import Block from './Block'; import Block from './Block';
import { Parsed, CompileOptions, Node } from '../../interfaces'; import { Parsed, CompileOptions, Node } from '../../interfaces';
@ -28,9 +29,10 @@ export class DomGenerator extends Generator {
parsed: Parsed, parsed: Parsed,
source: string, source: string,
name: string, name: string,
stylesheet: Stylesheet,
options: CompileOptions options: CompileOptions
) { ) {
super(parsed, source, name, options); super(parsed, source, name, stylesheet, options);
this.blocks = []; this.blocks = [];
this.readonly = new Set(); this.readonly = new Set();
@ -45,11 +47,12 @@ export class DomGenerator extends Generator {
export default function dom( export default function dom(
parsed: Parsed, parsed: Parsed,
source: string, source: string,
stylesheet: Stylesheet,
options: CompileOptions options: CompileOptions
) { ) {
const format = options.format || 'es'; const format = options.format || 'es';
const generator = new DomGenerator(parsed, source, options.name || 'SvelteComponent', options); const generator = new DomGenerator(parsed, source, options.name || 'SvelteComponent', stylesheet, options);
const { const {
computations, computations,

@ -1,5 +1,6 @@
import deindent from '../../utils/deindent'; import deindent from '../../utils/deindent';
import Generator from '../Generator'; import Generator from '../Generator';
import Stylesheet from '../Stylesheet';
import Block from './Block'; import Block from './Block';
import preprocess from './preprocess'; import preprocess from './preprocess';
import visit from './visit'; import visit from './visit';
@ -15,9 +16,10 @@ export class SsrGenerator extends Generator {
parsed: Parsed, parsed: Parsed,
source: string, source: string,
name: string, name: string,
stylesheet: Stylesheet,
options: CompileOptions options: CompileOptions
) { ) {
super(parsed, source, name, options); super(parsed, source, name, stylesheet, options);
this.bindings = []; this.bindings = [];
this.renderCode = ''; this.renderCode = '';
this.elementDepth = 0; this.elementDepth = 0;
@ -63,11 +65,12 @@ export class SsrGenerator extends Generator {
export default function ssr( export default function ssr(
parsed: Parsed, parsed: Parsed,
source: string, source: string,
stylesheet: Stylesheet,
options: CompileOptions options: CompileOptions
) { ) {
const format = options.format || 'cjs'; const format = options.format || 'cjs';
const generator = new SsrGenerator(parsed, source, options.name || 'SvelteComponent', options); const generator = new SsrGenerator(parsed, source, options.name || 'SvelteComponent', stylesheet, options);
const { computations, name, hasJs, templateProperties } = generator; const { computations, name, hasJs, templateProperties } = generator;

@ -4,6 +4,7 @@ import generate from './generators/dom/index';
import generateSSR from './generators/server-side-rendering/index'; import generateSSR from './generators/server-side-rendering/index';
import { assign } from './shared/index.js'; import { assign } from './shared/index.js';
import { version } from '../package.json'; import { version } from '../package.json';
import Stylesheet from './generators/Stylesheet';
import { Parsed, CompileOptions, Warning } from './interfaces'; import { Parsed, CompileOptions, Warning } from './interfaces';
function normalizeOptions(options: CompileOptions): CompileOptions { function normalizeOptions(options: CompileOptions): CompileOptions {
@ -44,11 +45,13 @@ export function compile(source: string, _options: CompileOptions) {
return; return;
} }
validate(parsed, source, options); const stylesheet = new Stylesheet(source, parsed, options.filename, options.cascade !== false);
validate(parsed, source, stylesheet, options);
const compiler = options.generate === 'ssr' ? generateSSR : generate; const compiler = options.generate === 'ssr' ? generateSSR : generate;
return compiler(parsed, source, options); return compiler(parsed, source, stylesheet, options);
} }
export function create(source: string, _options: CompileOptions = {}) { export function create(source: string, _options: CompileOptions = {}) {

@ -1,9 +1,5 @@
import { Node } from '../interfaces'; import { Node } from '../interfaces';
export function isGlobalSelector(block: Node[]) {
return block[0].type === 'PseudoClassSelector' && block[0].name === 'global';
}
export function groupSelectors(selector: Node) { export function groupSelectors(selector: Node) {
let block = { let block = {
global: selector.children[0].type === 'PseudoClassSelector' && selector.children[0].name === 'global', global: selector.children[0].type === 'PseudoClassSelector' && selector.children[0].name === 'global',

@ -1,4 +1,4 @@
import { groupSelectors, isGlobalSelector, walkRules } from '../../utils/css'; import { groupSelectors, walkRules } from '../../utils/css';
import { Validator } from '../index'; import { Validator } from '../index';
import { Node } from '../../interfaces'; import { Node } from '../../interfaces';

@ -4,6 +4,7 @@ import validateHtml from './html/index';
import { getLocator, Location } from 'locate-character'; import { getLocator, Location } from 'locate-character';
import getCodeFrame from '../utils/getCodeFrame'; import getCodeFrame from '../utils/getCodeFrame';
import CompileError from '../utils/CompileError'; import CompileError from '../utils/CompileError';
import Stylesheet from '../generators/Stylesheet';
import { Node, Parsed, CompileOptions, Warning } from '../interfaces'; import { Node, Parsed, CompileOptions, Warning } from '../interfaces';
class ValidationError extends CompileError { class ValidationError extends CompileError {
@ -73,6 +74,7 @@ export class Validator {
export default function validate( export default function validate(
parsed: Parsed, parsed: Parsed,
source: string, source: string,
stylesheet: Stylesheet,
options: CompileOptions options: CompileOptions
) { ) {
const { onwarn, onerror, name, filename } = options; const { onwarn, onerror, name, filename } = options;
@ -103,7 +105,7 @@ export default function validate(
} }
if (parsed.css) { if (parsed.css) {
validateCss(validator, parsed.css); stylesheet.validate(validator);
} }
if (parsed.html) { if (parsed.html) {

@ -18,12 +18,10 @@ describe("validate", () => {
const input = fs.readFileSync(filename, "utf-8").replace(/\s+$/, ""); const input = fs.readFileSync(filename, "utf-8").replace(/\s+$/, "");
try { try {
const parsed = svelte.parse(input);
const errors = []; const errors = [];
const warnings = []; const warnings = [];
svelte.validate(parsed, input, { svelte.compile(input, {
onerror(error) { onerror(error) {
errors.push({ errors.push({
message: error.message, message: error.message,

Loading…
Cancel
Save