From 27f7df4db83a35401603b85c4fe35b9ea7b342c0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 8 Sep 2018 22:13:15 -0400 Subject: [PATCH] move files around a bit --- src/compile/dom/Block.ts | 1 - src/compile/dom/index.ts | 9 +-- src/compile/index.ts | 69 +++++++++++++++++ src/index.ts | 158 +++------------------------------------ src/interfaces.ts | 13 +--- src/parse/index.ts | 8 +- src/preprocess/index.ts | 100 +++++++++++++++++++++++++ test/preprocess/index.js | 2 +- 8 files changed, 186 insertions(+), 174 deletions(-) create mode 100644 src/compile/index.ts create mode 100644 src/preprocess/index.ts diff --git a/src/compile/dom/Block.ts b/src/compile/dom/Block.ts index ca3984b5d7..5024b636a2 100644 --- a/src/compile/dom/Block.ts +++ b/src/compile/dom/Block.ts @@ -2,7 +2,6 @@ import CodeBuilder from '../../utils/CodeBuilder'; import deindent from '../../utils/deindent'; import { escape } from '../../utils/stringify'; import Compiler from '../Compiler'; -import { Node } from '../../interfaces'; export interface BlockOptions { parent?: Block; diff --git a/src/compile/dom/index.ts b/src/compile/dom/index.ts index 3ced0254a2..d3e98e38fb 100644 --- a/src/compile/dom/index.ts +++ b/src/compile/dom/index.ts @@ -1,19 +1,12 @@ -import MagicString from 'magic-string'; -import isReference from 'is-reference'; -import { parseExpressionAt } from 'acorn'; -import annotateWithScopes from '../../utils/annotateWithScopes'; -import { walk } from 'estree-walker'; import deindent from '../../utils/deindent'; import { stringify, escape } from '../../utils/stringify'; import CodeBuilder from '../../utils/CodeBuilder'; import globalWhitelist from '../../utils/globalWhitelist'; -import reservedNames from '../../utils/reservedNames'; import Compiler from '../Compiler'; import Stylesheet from '../../css/Stylesheet'; import Stats from '../../Stats'; import Block from './Block'; -import { test } from '../../config'; -import { Ast, CompileOptions, Node } from '../../interfaces'; +import { Ast, CompileOptions } from '../../interfaces'; export class DomTarget { blocks: (Block|string)[]; diff --git a/src/compile/index.ts b/src/compile/index.ts new file mode 100644 index 0000000000..cfb1b9e832 --- /dev/null +++ b/src/compile/index.ts @@ -0,0 +1,69 @@ +import { assign } from '../shared'; +import Stats from '../Stats'; +import parse from '../parse/index'; +import Stylesheet from '../css/Stylesheet'; +import validate from '../validate'; +import generate from './dom/index'; +import generateSSR from './ssr/index'; +import { CompileOptions, Warning, Ast } from '../interfaces'; + +function normalize_options(options: CompileOptions): CompileOptions { + let normalized = assign({ generate: 'dom' }, options); + const { onwarn, onerror } = normalized; + + normalized.onwarn = onwarn + ? (warning: Warning) => onwarn(warning, default_onwarn) + : default_onwarn; + + normalized.onerror = onerror + ? (error: Error) => onerror(error, default_onerror) + : default_onerror; + + return normalized; +} + +function default_onwarn({ start, message }: Warning) { + if (start) { + console.warn(`(${start.line}:${start.column}) – ${message}`); + } else { + console.warn(message); + } +} + +function default_onerror(error: Error) { + throw error; +} + +export default function compile(source: string, _options: CompileOptions) { + const options = normalize_options(_options); + let ast: Ast; + + const stats = new Stats({ + onwarn: options.onwarn + }); + + try { + stats.start('parse'); + ast = parse(source, options); + stats.stop('parse'); + } catch (err) { + options.onerror(err); + return; + } + + stats.start('stylesheet'); + const stylesheet = new Stylesheet(source, ast, options.filename, options.dev); + stats.stop('stylesheet'); + + stats.start('validate'); + validate(ast, source, stylesheet, stats, options); + stats.stop('validate'); + + if (options.generate === false) { + return { ast, stats: stats.render(null), js: null, css: null }; + } + + const compiler = options.generate === 'ssr' ? generateSSR : generate; + + return compiler(ast, source, stylesheet, options, stats); +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 2895dd2977..6c1949e472 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,149 +1,10 @@ -import parse from './parse/index'; -import validate from './validate/index'; -import generate from './compile/dom/index'; -import generateSSR from './compile/ssr/index'; -import Stats from './Stats'; -import { assign } from './shared/index.js'; -import Stylesheet from './css/Stylesheet'; -import { Ast, CompileOptions, Warning, PreprocessOptions, Preprocessor } from './interfaces'; -import { SourceMap } from 'magic-string'; +import compile from './compile/index'; +import { CompileOptions } from './interfaces'; -const version = '__VERSION__'; +export function create(source: string, options: CompileOptions = {}) { + options.format = 'eval'; -function normalizeOptions(options: CompileOptions): CompileOptions { - let normalizedOptions = assign({ generate: 'dom' }, options); - const { onwarn, onerror } = normalizedOptions; - normalizedOptions.onwarn = onwarn - ? (warning: Warning) => onwarn(warning, defaultOnwarn) - : defaultOnwarn; - normalizedOptions.onerror = onerror - ? (error: Error) => onerror(error, defaultOnerror) - : defaultOnerror; - return normalizedOptions; -} - -function defaultOnwarn(warning: Warning) { - if (warning.start) { - console.warn( - `(${warning.start.line}:${warning.start.column}) – ${warning.message}` - ); // eslint-disable-line no-console - } else { - console.warn(warning.message); // eslint-disable-line no-console - } -} - -function defaultOnerror(error: Error) { - throw error; -} - -function parseAttributeValue(value: string) { - return /^['"]/.test(value) ? - value.slice(1, -1) : - value; -} - -function parseAttributes(str: string) { - const attrs = {}; - str.split(/\s+/).filter(Boolean).forEach(attr => { - const [name, value] = attr.split('='); - attrs[name] = value ? parseAttributeValue(value) : true; - }); - return attrs; -} - -async function replaceTagContents(source, type: 'script' | 'style', preprocessor: Preprocessor, options: PreprocessOptions) { - const exp = new RegExp(`<${type}([\\S\\s]*?)>([\\S\\s]*?)<\\/${type}>`, 'ig'); - const match = exp.exec(source); - - if (match) { - const attributes: Record = parseAttributes(match[1]); - const content: string = match[2]; - const processed: { code: string, map?: SourceMap | string } = await preprocessor({ - content, - attributes, - filename : options.filename - }); - - if (processed && processed.code) { - return ( - source.slice(0, match.index) + - `<${type}>${processed.code}` + - source.slice(match.index + match[0].length) - ); - } - } - - return source; -} - -export async function preprocess(source: string, options: PreprocessOptions) { - const { markup, style, script } = options; - if (!!markup) { - const processed: { code: string, map?: SourceMap | string } = await markup({ - content: source, - filename: options.filename - }); - source = processed.code; - } - - if (!!style) { - source = await replaceTagContents(source, 'style', style, options); - } - - if (!!script) { - source = await replaceTagContents(source, 'script', script, options); - } - - return { - // TODO return separated output, in future version where svelte.compile supports it: - // style: { code: styleCode, map: styleMap }, - // script { code: scriptCode, map: scriptMap }, - // markup { code: markupCode, map: markupMap }, - - toString() { - return source; - } - }; -} - -function compile(source: string, _options: CompileOptions) { - const options = normalizeOptions(_options); - let ast: Ast; - - const stats = new Stats({ - onwarn: options.onwarn - }); - - try { - stats.start('parse'); - ast = parse(source, options); - stats.stop('parse'); - } catch (err) { - options.onerror(err); - return; - } - - stats.start('stylesheet'); - const stylesheet = new Stylesheet(source, ast, options.filename, options.dev); - stats.stop('stylesheet'); - - stats.start('validate'); - validate(ast, source, stylesheet, stats, options); - stats.stop('validate'); - - if (options.generate === false) { - return { ast, stats: stats.render(null), js: null, css: null }; - } - - const compiler = options.generate === 'ssr' ? generateSSR : generate; - - return compiler(ast, source, stylesheet, options, stats); -}; - -function create(source: string, _options: CompileOptions = {}) { - _options.format = 'eval'; - - const compiled = compile(source, _options); + const compiled = compile(source, options); if (!compiled || !compiled.js.code) { return; @@ -152,8 +13,8 @@ function create(source: string, _options: CompileOptions = {}) { try { return (new Function(`return ${compiled.js.code}`))(); } catch (err) { - if (_options.onerror) { - _options.onerror(err); + if (options.onerror) { + options.onerror(err); return; } else { throw err; @@ -161,4 +22,7 @@ function create(source: string, _options: CompileOptions = {}) { } } -export { parse, create, compile, version as VERSION }; +export { default as compile } from './compile/index'; +export { default as parse } from './parse/index'; +export { default as preprocess } from './preprocess/index'; +export const VERSION = '__VERSION__'; \ No newline at end of file diff --git a/src/interfaces.ts b/src/interfaces.ts index a30a67a91f..bb7b7e2fb2 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -1,5 +1,3 @@ -import {SourceMap} from 'magic-string'; - export interface Node { start: number; end: number; @@ -67,7 +65,7 @@ export interface CompileOptions { // to remove in v3 skipIntroByDefault?: boolean; - nestedTransitions: boolean; + nestedTransitions?: boolean; } export interface GenerateOptions { @@ -92,15 +90,6 @@ export interface CustomElementOptions { props?: string[]; } -export interface PreprocessOptions { - markup?: (options: {content: string, filename: string}) => { code: string, map?: SourceMap | string }; - style?: Preprocessor; - script?: Preprocessor; - filename?: string -} - -export type Preprocessor = (options: {content: string, attributes: Record, filename?: string}) => { code: string, map?: SourceMap | string }; - export interface AppendTarget { slots: Record; slotStack: string[] diff --git a/src/parse/index.ts b/src/parse/index.ts index 6fb40193d6..f81a341273 100644 --- a/src/parse/index.ts +++ b/src/parse/index.ts @@ -1,17 +1,15 @@ import { isIdentifierStart, isIdentifierChar } from 'acorn'; -import { locate, Location } from 'locate-character'; import fragment from './state/fragment'; import { whitespace } from '../utils/patterns'; -import { trimStart, trimEnd } from '../utils/trim'; import reservedNames from '../utils/reservedNames'; import fullCharCodeAt from '../utils/fullCharCodeAt'; -import { Node, Ast } from '../interfaces'; +import { Node, Ast, CustomElementOptions } from '../interfaces'; import error from '../utils/error'; interface ParserOptions { filename?: string; bind?: boolean; - customElement?: boolean; + customElement?: CustomElementOptions | true; } type ParserState = (parser: Parser) => (ParserState | void); @@ -19,7 +17,7 @@ type ParserState = (parser: Parser) => (ParserState | void); export class Parser { readonly template: string; readonly filename?: string; - readonly customElement: boolean; + readonly customElement: CustomElementOptions | true; index: number; stack: Array; diff --git a/src/preprocess/index.ts b/src/preprocess/index.ts new file mode 100644 index 0000000000..047b124991 --- /dev/null +++ b/src/preprocess/index.ts @@ -0,0 +1,100 @@ +import { SourceMap } from 'magic-string'; + +export interface PreprocessOptions { + markup?: (options: { + content: string, + filename: string + }) => { code: string, map?: SourceMap | string }; + style?: Preprocessor; + script?: Preprocessor; + filename?: string +} + +export type Preprocessor = (options: { + content: string, + attributes: Record, + filename?: string +}) => { code: string, map?: SourceMap | string }; + +function parseAttributeValue(value: string) { + return /^['"]/.test(value) ? + value.slice(1, -1) : + value; +} + +function parseAttributes(str: string) { + const attrs = {}; + str.split(/\s+/).filter(Boolean).forEach(attr => { + const [name, value] = attr.split('='); + attrs[name] = value ? parseAttributeValue(value) : true; + }); + return attrs; +} + +async function replaceTagContents( + source, + type: 'script' | 'style', + preprocessor: Preprocessor, + options: PreprocessOptions +) { + const exp = new RegExp(`<${type}([\\S\\s]*?)>([\\S\\s]*?)<\\/${type}>`, 'ig'); + const match = exp.exec(source); + + if (match) { + const attributes: Record = parseAttributes(match[1]); + const content: string = match[2]; + const processed: { code: string, map?: SourceMap | string } = await preprocessor({ + content, + attributes, + filename : options.filename + }); + + if (processed && processed.code) { + return ( + source.slice(0, match.index) + + `<${type}>${processed.code}` + + source.slice(match.index + match[0].length) + ); + } + } + + return source; +} + +export default async function preprocess( + source: string, + options: PreprocessOptions +) { + const { markup, style, script } = options; + + if (!!markup) { + const processed: { + code: string, + map?: SourceMap | string + } = await markup({ + content: source, + filename: options.filename + }); + + source = processed.code; + } + + if (!!style) { + source = await replaceTagContents(source, 'style', style, options); + } + + if (!!script) { + source = await replaceTagContents(source, 'script', script, options); + } + + return { + // TODO return separated output, in future version where svelte.compile supports it: + // style: { code: styleCode, map: styleMap }, + // script { code: scriptCode, map: scriptMap }, + // markup { code: markupCode, map: markupMap }, + + toString() { + return source; + } + }; +} \ No newline at end of file diff --git a/test/preprocess/index.js b/test/preprocess/index.js index 6ed452f672..044f6965e6 100644 --- a/test/preprocess/index.js +++ b/test/preprocess/index.js @@ -1,5 +1,5 @@ import assert from 'assert'; -import {svelte} from '../helpers.js'; +import { svelte } from '../helpers.js'; describe('preprocess', () => { it('preprocesses entire component', () => {