├── examples ├── loader │ ├── .gitignore │ ├── views │ │ └── view.bjs │ ├── webpack.config.js │ ├── package.json │ └── src │ │ └── index.js └── express │ ├── views │ ├── index.bjs │ └── layouts │ │ └── app.bjs │ ├── package.json │ └── src │ └── index.ts ├── packages ├── bladejs │ ├── .npmignore │ ├── src │ │ ├── util │ │ │ ├── asyncIterator.ts │ │ │ ├── htmlspecialchars.ts │ │ │ └── trimAsyncIterable.ts │ │ ├── runtime │ │ │ ├── RuntimeFunction.ts │ │ │ ├── RuntimeFilter.ts │ │ │ └── Runtime.ts │ │ ├── template │ │ │ ├── CompiledTemplate.ts │ │ │ └── TemplateProvider.ts │ │ ├── index.ts │ │ └── environment │ │ │ ├── Environment.ts │ │ │ └── DefaultEnvironment.ts │ ├── tsconfig.json │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── test │ │ └── util │ │ │ └── trimAsyncIterable.test.ts │ └── yarn.lock ├── bladejs-compiler │ ├── .npmignore │ ├── src │ │ ├── cli │ │ │ ├── Output.ts │ │ │ ├── StdOutput.ts │ │ │ └── bladejs-compiler.ts │ │ ├── error │ │ │ ├── LexerError.ts │ │ │ ├── ParserError.ts │ │ │ ├── CompilerError.ts │ │ │ └── AbstractError.ts │ │ ├── compiler │ │ │ ├── vue-template-es2015-compiler.d.ts │ │ │ ├── CompilerDelegate.ts │ │ │ └── Compiler.ts │ │ ├── plugin │ │ │ ├── CompilerPlugin.ts │ │ │ ├── JsBlockCompilerPlugin.ts │ │ │ ├── JsonCompilerPlugin.ts │ │ │ ├── IncludesCompilerPlugin.ts │ │ │ ├── StacksCompilerPlugin.ts │ │ │ ├── ComponentsCompilerPlugin.ts │ │ │ ├── LayoutCompilerPlugin.ts │ │ │ ├── ConditionalsCompilerPlugin.ts │ │ │ └── LoopsCompilerPlugin.ts │ │ ├── source │ │ │ ├── Location.ts │ │ │ ├── Source.ts │ │ │ └── Position.ts │ │ ├── string │ │ │ ├── Char.ts │ │ │ └── StringBuilder.ts │ │ ├── index.ts │ │ ├── lexer │ │ │ ├── Token.ts │ │ │ └── Lexer.ts │ │ ├── scanner │ │ │ └── Scanner.ts │ │ └── parser │ │ │ ├── Node.ts │ │ │ └── Parser.ts │ ├── tsconfig.json │ ├── bin │ │ ├── bladejs-c │ │ └── bladejs-compiler │ ├── test │ │ ├── error │ │ │ └── AbstractError.test.ts │ │ ├── types │ │ │ ├── Source.test.ts │ │ │ └── Position.test.ts │ │ ├── string │ │ │ ├── StringBuilder.test.ts │ │ │ └── Char.test.ts │ │ ├── util.test.ts │ │ ├── scanner │ │ │ └── Scanner.test.ts │ │ ├── parser │ │ │ └── Parser.test.ts │ │ └── lexer │ │ │ └── Lexer.test.ts │ ├── LICENSE │ ├── README.md │ └── package.json └── bladejs-loader │ ├── .npmignore │ ├── tsconfig.json │ ├── LICENSE │ ├── README.md │ ├── package.json │ ├── src │ └── index.ts │ └── yarn.lock ├── .gitignore ├── .editorconfig ├── .vscode └── settings.json ├── tslint.json ├── tsconfig.json ├── appveyor.yml ├── LICENSE ├── .travis.yml └── README.md /examples/loader/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | -------------------------------------------------------------------------------- /packages/bladejs/.npmignore: -------------------------------------------------------------------------------- 1 | /lib/.tsbuildinfo 2 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/.npmignore: -------------------------------------------------------------------------------- 1 | /lib/.tsbuildinfo 2 | -------------------------------------------------------------------------------- /packages/bladejs-loader/.npmignore: -------------------------------------------------------------------------------- 1 | /lib/.tsbuildinfo 2 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/cli/Output.ts: -------------------------------------------------------------------------------- 1 | export interface Output { 2 | write(str: string): Promise; 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /examples/*/node_modules 3 | /examples/*/yarn.lock 4 | /packages/*/node_modules 5 | /packages/*/lib 6 | yarn-error.log 7 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/error/LexerError.ts: -------------------------------------------------------------------------------- 1 | import { AbstractError } from './AbstractError'; 2 | 3 | export class LexerError extends AbstractError { 4 | } 5 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/error/ParserError.ts: -------------------------------------------------------------------------------- 1 | import { AbstractError } from './AbstractError'; 2 | 3 | export class ParserError extends AbstractError { 4 | } 5 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/error/CompilerError.ts: -------------------------------------------------------------------------------- 1 | import { AbstractError } from './AbstractError'; 2 | 3 | export class CompilerError extends AbstractError { 4 | } 5 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/compiler/vue-template-es2015-compiler.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vue-template-es2015-compiler' { 2 | const value: any; 3 | export = value; 4 | } 5 | -------------------------------------------------------------------------------- /packages/bladejs/src/util/asyncIterator.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | 3 | if (!('asyncIterator' in Symbol)) { 4 | (Symbol as any).asyncIterator = (Symbol as any).for('asyncIterator'); 5 | } 6 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/CompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | 3 | export interface CompilerPlugin { 4 | init(compiler: Compiler): void; 5 | } 6 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/source/Location.ts: -------------------------------------------------------------------------------- 1 | import { Position } from './Position'; 2 | 3 | export type Location = { 4 | readonly start: Position; 5 | readonly end: Position; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/bladejs/src/runtime/RuntimeFunction.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from '../environment/Environment'; 2 | 3 | export type RuntimeFunction = (env: Environment, ...args: any[]) => AsyncIterable; 4 | -------------------------------------------------------------------------------- /packages/bladejs/src/template/CompiledTemplate.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from '../environment/Environment'; 2 | 3 | export type CompiledTemplate = (this: Environment, env: Environment) => AsyncIterable; 4 | -------------------------------------------------------------------------------- /packages/bladejs/src/runtime/RuntimeFilter.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from '../environment/Environment'; 2 | 3 | export type RuntimeFilter = (env: Environment, source: any, ...args: any[]) => PromiseLike | string; 4 | -------------------------------------------------------------------------------- /examples/loader/views/view.bjs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | App Name - Title 5 | 6 | 7 |
8 | {{ myVar }} 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.{yml,json,js,ts}] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/string/Char.ts: -------------------------------------------------------------------------------- 1 | export const Char = { 2 | isLetter: (c: any) => typeof c === 'string' && c.toLowerCase() !== c.toUpperCase(), 3 | isWhitespace: (c: any) => typeof c === 'string' && /^[ \t]+$/.test(c), 4 | }; 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/.DS_Store": true, 5 | "**/lib": true, 6 | "**/node_modules": true 7 | }, 8 | "prettier.singleQuote": true, 9 | "prettier.useTabs": false, 10 | "prettier.tabWidth": 2, 11 | "prettier.semi": true, 12 | "prettier.trailingComma": "all" 13 | } 14 | -------------------------------------------------------------------------------- /packages/bladejs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 6 | "baseUrl": "./src" 7 | }, 8 | "include": [ 9 | "src" 10 | ], 11 | "exclude": [ 12 | "node_modules", 13 | "test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /examples/express/views/index.bjs: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | @section('title', 'Page Title') 4 | 5 | @section('sidebar') 6 | @parent 7 | 8 | {{ myVar }} 9 | 10 |

This is appended to the master sidebar.

11 | @endsection 12 | 13 | @section('content') 14 |

This is my body content.

15 | @endsection 16 | -------------------------------------------------------------------------------- /packages/bladejs-loader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 6 | "baseUrl": "./src" 7 | }, 8 | "include": [ 9 | "src" 10 | ], 11 | "exclude": [ 12 | "node_modules", 13 | "test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "tsBuildInfoFile": "./lib/.tsbuildinfo", 6 | "baseUrl": "./src" 7 | }, 8 | "include": [ 9 | "src" 10 | ], 11 | "exclude": [ 12 | "node_modules", 13 | "test" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /examples/loader/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'development', 3 | entry: './src/index.js', 4 | module: { 5 | rules: [ 6 | { 7 | test: /\.bjs$/, 8 | use: [ 9 | { 10 | loader: '@tarik02/bladejs-loader', 11 | }, 12 | ], 13 | }, 14 | ], 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/cli/StdOutput.ts: -------------------------------------------------------------------------------- 1 | import { Output } from './Output'; 2 | 3 | export class StdOutput implements Output { 4 | public write(str: string): Promise { 5 | return new Promise((resolve, reject) => { 6 | process.stdout.write(str, (error: any) => error ? reject(error) : resolve()); 7 | }); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/bin/bladejs-c: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../lib/cli/bladejs-compiler.js').default( 3 | require('path').basename(__filename), 4 | process.argv.slice(2), 5 | new (require('../lib/cli/StdOutput.js').StdOutput), 6 | ).then(process.exit, error => { 7 | console.error(error); 8 | process.exit(-1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/bin/bladejs-compiler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../lib/cli/bladejs-compiler.js').default( 3 | require('path').basename(__filename), 4 | process.argv.slice(2), 5 | new (require('../lib/cli/StdOutput.js').StdOutput), 6 | ).then(process.exit, error => { 7 | console.error(error); 8 | process.exit(-1); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/string/StringBuilder.ts: -------------------------------------------------------------------------------- 1 | export interface StringBuilder { 2 | append(arg: string): void; 3 | 4 | build(): string; 5 | } 6 | 7 | export const createStringBuilder = (): StringBuilder => { 8 | let buffer = ''; 9 | 10 | return { 11 | append: arg => { 12 | buffer += arg; 13 | }, 14 | 15 | build: () => buffer, 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /examples/express/views/layouts/app.bjs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | App Name - @yield('title') 5 | 6 | 7 | @section('sidebar') 8 | This is the master sidebar. 9 | @show 10 | 11 |
12 | @yield('content') 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-loader", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "private": true, 6 | "dependencies": { 7 | "@tarik02/bladejs": "^3.0.0" 8 | }, 9 | "devDependencies": { 10 | "@tarik02/bladejs-compiler": "^3.0.0", 11 | "@tarik02/bladejs-loader": "^3.0.0", 12 | "webpack": "^4.35.3", 13 | "webpack-cli": "^3.3.6" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/bladejs/src/util/htmlspecialchars.ts: -------------------------------------------------------------------------------- 1 | const MAP = { 2 | '&': '&', 3 | '<': '<', 4 | '>': '>', 5 | '"': '"', 6 | '\'': ''', 7 | }; 8 | 9 | const replacer = ( 10 | (match: keyof typeof MAP): string => MAP[match] 11 | ) as ((match: string) => string); 12 | 13 | export const htmlspecialchars = (value: any) => { 14 | return `${value}`.replace(/[&<>"']/g, replacer); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/JsBlockCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | import { CompilerPlugin } from './CompilerPlugin'; 3 | 4 | export class JsBlockCompilerPlugin implements CompilerPlugin { 5 | public init(compiler: Compiler): void { 6 | compiler.addRawFunction('js', (delegate, node) => { 7 | delegate.expectedArgs(node, 0); 8 | delegate.append(node.content); 9 | }); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-express", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "private": true, 6 | "dependencies": { 7 | "express": "^4.17.1", 8 | "fs-extra": "^8.1.0", 9 | "tslib": "^1.10.0" 10 | }, 11 | "devDependencies": { 12 | "@types/express": "^4.17.0", 13 | "@types/fs-extra": "^8.0.0", 14 | "@types/node": "^12.6.3", 15 | "ts-node": "^10.0.0", 16 | "typescript": "^4.3.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/bladejs/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './environment/DefaultEnvironment'; 2 | export * from './environment/Environment'; 3 | export * from './runtime/Runtime'; 4 | export * from './runtime/RuntimeFilter'; 5 | export * from './runtime/RuntimeFunction'; 6 | export * from './template/CompiledTemplate'; 7 | export * from './template/TemplateProvider'; 8 | export * from './util/asyncIterator'; 9 | export * from './util/htmlspecialchars'; 10 | export * from './util/trimAsyncIterable'; 11 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/JsonCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | import { CompilerPlugin } from './CompilerPlugin'; 3 | 4 | export class JsonCompilerPlugin implements CompilerPlugin { 5 | public init(compiler: Compiler): void { 6 | compiler.addFunction('json', (delegate, node) => { 7 | delegate.expectedArgs(node, 1, 3); 8 | 9 | delegate.append('yield JSON.stringify('); 10 | delegate.append(node.args!.join(', ')); 11 | delegate.append(');'); 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/IncludesCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | import { CompilerPlugin } from './CompilerPlugin'; 3 | 4 | export class IncludesCompilerPlugin implements CompilerPlugin { 5 | public init(compiler: Compiler): void { 6 | compiler.addSimpleFunction('include', 1, 2); 7 | compiler.addSimpleFunction('includeIf', 1, 2); 8 | compiler.addSimpleFunction('includeWhen', 2, 3); 9 | compiler.addSimpleFunction('includeFirst', 1, 2); 10 | 11 | compiler.addSimpleFunction('each', 3, 4); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/error/AbstractError.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import 'mocha'; 3 | 4 | import { lexError } from '../util.test'; 5 | 6 | describe('error/AbstractError', () => { 7 | describe('prettyPrint', () => { 8 | it('error-1', () => { 9 | expect(lexError(` 10 | 11 | 12 | @section('sidebar', {)) 13 | This is the master sidebar. 14 | @show 15 | 16 | 17 | `.trim()).prettyPrint(false)).eq(` 18 | unknown:3:30: expected "}", got ")" 19 | 2. 20 | 3. @section('sidebar', {)) 21 | ^ 22 | 4. This is the master sidebar. 23 | `.trim() + '\n'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "quotemark": [ 9 | true, 10 | "single" 11 | ], 12 | "ban-types": false, 13 | "interface-over-type-literal": false, 14 | "arrow-parens": [ 15 | true, 16 | "ban-single-arg-parens" 17 | ], 18 | "object-literal-sort-keys": false, 19 | "no-string-literal": false, 20 | "interface-name": false, 21 | "member-ordering": false, 22 | "no-bitwise": false, 23 | "variable-name": false, 24 | "no-conditional-assignment": false, 25 | "no-unused-expression": true, 26 | "space-before-function-paren": false 27 | }, 28 | "rulesDirectory": [] 29 | } 30 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/types/Source.test.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-expression */ 2 | 3 | import { expect } from 'chai'; 4 | import 'mocha'; 5 | 6 | import { Source } from '../../src/source/Source'; 7 | 8 | describe('types/Source', () => { 9 | describe('getLine', () => { 10 | it('should return line by index starting with 1', () => { 11 | const source = new Source(` 12 | Line 1 13 | Line 2 14 | Line 3 15 | Line 4 16 | Line 5 17 | `.trim()); 18 | 19 | expect(source.getLine(0)).eq(undefined); 20 | expect(source.getLine(1)).eq('Line 1'); 21 | expect(source.getLine(2)).eq('Line 2'); 22 | expect(source.getLine(3)).eq('Line 3'); 23 | expect(source.getLine(4)).eq('Line 4'); 24 | expect(source.getLine(5)).eq('Line 5'); 25 | expect(source.getLine(6)).eq(undefined); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /examples/loader/src/index.js: -------------------------------------------------------------------------------- 1 | import view from '../views/view.bjs'; 2 | import { Runtime } from '@tarik02/bladejs'; 3 | 4 | const runtime = new Runtime([ 5 | { 6 | async getTemplateCompiledFile(name) { 7 | if (name === 'view') { 8 | return view; 9 | } 10 | 11 | return undefined; 12 | }, 13 | 14 | async isOutdated(name, template, creationTime) { 15 | return false; 16 | } 17 | }, 18 | ]); 19 | 20 | (async () => { 21 | if (typeof document !== 'undefined') { 22 | let data = ''; 23 | for await (const chunk of runtime.render('view', { 24 | myVar: 'My variable value', 25 | })) { 26 | data += chunk; 27 | } 28 | document.write(data); 29 | } else { 30 | for await (const chunk of runtime.render('view', { 31 | myVar: 'My variable value', 32 | })) { 33 | global.process.stdout.write(chunk); 34 | } 35 | } 36 | })(); 37 | -------------------------------------------------------------------------------- /packages/bladejs/src/util/trimAsyncIterable.ts: -------------------------------------------------------------------------------- 1 | import './asyncIterator'; 2 | 3 | export const trimAsyncIterable = async function *(iteratee: AsyncIterable): AsyncIterable { 4 | const iterator = iteratee[Symbol.asyncIterator](); 5 | 6 | let tmp: string; 7 | 8 | // Trim left 9 | do { 10 | const item = await iterator.next(); 11 | if (item.done) { 12 | return; 13 | } 14 | 15 | tmp = item.value.trimLeft(); 16 | } while (tmp.length === 0); 17 | 18 | // Trim right 19 | let whitespace = ''; 20 | while (true) { 21 | const trimmed = tmp.trimRight(); 22 | if (trimmed.length === 0) { 23 | whitespace += tmp; 24 | } else { 25 | yield whitespace; 26 | yield trimmed; 27 | whitespace = tmp.substring(trimmed.length); 28 | } 29 | 30 | const item = await iterator.next(); 31 | if (item.done) { 32 | return; 33 | } 34 | tmp = item.value; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /packages/bladejs/src/template/TemplateProvider.ts: -------------------------------------------------------------------------------- 1 | import { CompiledTemplate } from './CompiledTemplate'; 2 | 3 | export interface TemplateProvider { 4 | /** 5 | * This function should return async generator function which yields chunks of text. 6 | * The returned function gets only one argument - {Environment}. 7 | * 8 | * @param name The name of the template 9 | * @return {Promise} Compiled template content or undefined if not found 10 | */ 11 | getTemplateCompiledFile(name: string): Promise; 12 | 13 | /** 14 | * @param name The name of the template 15 | * @param template The result of corresponding {getTemplateCompiledFile} call 16 | * @param creationTime The time (Date.now) of corresponding {getTemplateCompiledFile} call 17 | * @return Whether the template is already outdated 18 | */ 19 | isOutdated(name: string, template: CompiledTemplate, creationTime: number): Promise; 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "target": "es5", 5 | "module": "commonjs", 6 | "lib": [ 7 | "es6", 8 | "es7", 9 | "dom", 10 | "esnext.asynciterable" 11 | ], 12 | "allowJs": false, 13 | "sourceMap": true, 14 | "removeComments": false, 15 | "importHelpers": true, 16 | "downlevelIteration": true, 17 | "declaration": true, 18 | 19 | "strict": true, 20 | "noImplicitAny": true, 21 | "strictNullChecks": true, 22 | "strictFunctionTypes": true, 23 | "strictBindCallApply": true, 24 | "strictPropertyInitialization": true, 25 | "noImplicitThis": true, 26 | "alwaysStrict": true, 27 | 28 | "noUnusedLocals": true, 29 | "noUnusedParameters": true, 30 | "noImplicitReturns": true, 31 | "noFallthroughCasesInSwitch": true, 32 | 33 | "moduleResolution": "node", 34 | 35 | "experimentalDecorators": false, 36 | "emitDecoratorMetadata": false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './compiler/Compiler'; 2 | export * from './compiler/CompilerDelegate'; 3 | export * from './error/AbstractError'; 4 | export * from './error/CompilerError'; 5 | export * from './error/LexerError'; 6 | export * from './error/ParserError'; 7 | export * from './lexer/Lexer'; 8 | export * from './lexer/Token'; 9 | export * from './parser/Node'; 10 | export * from './parser/Parser'; 11 | export * from './plugin/CompilerPlugin'; 12 | export * from './plugin/ComponentsCompilerPlugin'; 13 | export * from './plugin/ConditionalsCompilerPlugin'; 14 | export * from './plugin/IncludesCompilerPlugin'; 15 | export * from './plugin/JsBlockCompilerPlugin'; 16 | export * from './plugin/JsonCompilerPlugin'; 17 | export * from './plugin/LayoutCompilerPlugin'; 18 | export * from './plugin/LoopsCompilerPlugin'; 19 | export * from './plugin/StacksCompilerPlugin'; 20 | export * from './scanner/Scanner'; 21 | export * from './source/Location'; 22 | export * from './source/Position'; 23 | export * from './source/Source'; 24 | export * from './string/Char'; 25 | export * from './string/StringBuilder'; 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '8' 4 | current_package: 'bladejs' 5 | - nodejs_version: '8' 6 | current_package: 'bladejs-compiler' 7 | - nodejs_version: '8' 8 | current_package: 'bladejs-loader' 9 | - nodejs_version: '10' 10 | current_package: 'bladejs' 11 | - nodejs_version: '10' 12 | current_package: 'bladejs-compiler' 13 | - nodejs_version: '10' 14 | current_package: 'bladejs-loader' 15 | - nodejs_version: '12' 16 | current_package: 'bladejs' 17 | - nodejs_version: '12' 18 | current_package: 'bladejs-compiler' 19 | - nodejs_version: '12' 20 | current_package: 'bladejs-loader' 21 | 22 | install: 23 | - ps: Install-Product node $env:nodejs_version 24 | - npm install --global yarn@latest 25 | - set PATH=%APPDATA%\npm;%PATH% 26 | 27 | - ps: cd packages/$env:current_package 28 | - yarn install 29 | 30 | matrix: 31 | fast_finish: true 32 | build: false 33 | shallow_clone: true 34 | 35 | test_script: 36 | - node --version 37 | - yarn --version 38 | 39 | - yarn lint 40 | - yarn build 41 | - yarn test 42 | 43 | cache: 44 | - '%APPDATA%\npm-cache' 45 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/compiler/CompilerDelegate.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BaseFunction, 3 | Node, 4 | NodeComment, 5 | NodeContainer, 6 | NodeData, 7 | NodeFunction, 8 | NodeRawFunction, NodeSequence, 9 | NodeText, 10 | } from '../parser/Node'; 11 | import { Location } from '../source/Location'; 12 | 13 | export interface CompilerDelegate { 14 | append(arg: string): void; 15 | footer: { 16 | append(arg: string): void; 17 | }; 18 | 19 | compileContainer(node: NodeContainer): void; 20 | compileComment(node: NodeComment): void; 21 | compileText(node: NodeText): void; 22 | compileData(node: NodeData): void; 23 | compileFunction(node: NodeFunction): void; 24 | compileRawFunction(node: NodeRawFunction): void; 25 | compileSequence(node: NodeSequence): void; 26 | compileNode(node: Node): void; 27 | 28 | error(message: string, node: Node, position?: Partial): never; 29 | 30 | expectedArgs(node: Node & BaseFunction, expected: number): never | void; 31 | // tslint:disable-next-line:unified-signatures 32 | expectedArgs(node: Node & BaseFunction, min: number, max: number): never | void; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Tarik02 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/string/StringBuilder.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import 'mocha'; 3 | import { createStringBuilder } from '../../src/string/StringBuilder'; 4 | 5 | describe('string/StringBuilder', () => { 6 | describe('createStringBuilder', () => { 7 | it('should return a valid StringBuilder instance', () => { 8 | const builder = createStringBuilder(); 9 | 10 | expect(builder).property('append'); 11 | expect(builder).property('build'); 12 | 13 | expect(typeof builder.append).eq('function'); 14 | expect(typeof builder.build).eq('function'); 15 | }); 16 | 17 | it('should return empty string if append was not called', () => { 18 | const builder = createStringBuilder(); 19 | expect(builder.build()).eq(''); 20 | }); 21 | 22 | it('should build string', () => { 23 | const builder = createStringBuilder(); 24 | 25 | builder.append('Hel'); 26 | builder.append('lo'); 27 | builder.append(' W'); 28 | builder.append('orl'); 29 | builder.append('d'); 30 | 31 | expect(builder.build()).eq('Hello World'); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/bladejs/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Tarik02 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Tarik02 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/bladejs-loader/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Tarik02 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/source/Source.ts: -------------------------------------------------------------------------------- 1 | export class Source { 2 | public readonly text: string; 3 | public readonly file: string | undefined; 4 | 5 | private readonly lineStartCache: number[] = [0]; 6 | 7 | public constructor(text: string, file?: string) { 8 | this.text = text; 9 | this.file = file; 10 | } 11 | 12 | public getLine(line: number): string | undefined { 13 | this.buildLineCache(line + 1); 14 | 15 | if (line < 1 || line >= this.lineStartCache.length) { 16 | return undefined; 17 | } 18 | 19 | return this.text.substring(this.lineStartCache[line - 1], this.lineStartCache[line] - 1); 20 | } 21 | 22 | private buildLineCache(line: number): void { 23 | const { text, lineStartCache } = this; 24 | 25 | while (line > lineStartCache.length) { 26 | let i = lineStartCache[lineStartCache.length - 1]; 27 | 28 | if (i === text.length + 1) { 29 | break; 30 | } 31 | 32 | for (; i < text.length; ++i) { 33 | if (text[i] === '\n') { 34 | lineStartCache.push(i + 1); 35 | } 36 | } 37 | 38 | if (i === text.length) { 39 | lineStartCache.push(i + 1); 40 | break; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/string/Char.test.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:object-literal-key-quotes */ 2 | /* tslint:disable:no-unused-expression */ 3 | 4 | import { expect } from 'chai'; 5 | import 'mocha'; 6 | import { inspect } from 'util'; 7 | 8 | import { Char } from '../../src/string/Char'; 9 | 10 | describe('string/Char', () => { 11 | const testWithBooleans = (fn: (value: V) => boolean, trueTests: V[], falseTests: V[]) => { 12 | for (const test of trueTests) { 13 | it('should return true for ' + inspect(test), () => { 14 | expect(fn(test)).true; 15 | }); 16 | } 17 | for (const test of falseTests) { 18 | it('should return false for ' + inspect(test), () => { 19 | expect(fn(test)).false; 20 | }); 21 | } 22 | }; 23 | 24 | describe('isLetter', () => { 25 | testWithBooleans(Char.isLetter, [ 26 | 'A', 27 | 'b', 28 | 'Z', 29 | 'ы', 30 | 'І', 31 | ], [ 32 | '1', 33 | '/', 34 | '(', 35 | '*', 36 | ' ', 37 | ]); 38 | }); 39 | 40 | describe('isWhitespace', () => { 41 | testWithBooleans(Char.isWhitespace, [ 42 | ' ', 43 | '\t', 44 | ' \t', 45 | '\t ', 46 | ], [ 47 | '1\t', 48 | ' p', 49 | '\n', 50 | '\r\n', 51 | ' A ', 52 | ]); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/lexer/Token.ts: -------------------------------------------------------------------------------- 1 | import { Location } from '../source/Location'; 2 | 3 | export type BaseToken = { 4 | readonly type: string; 5 | }; 6 | 7 | export type TokenComment = BaseToken & { 8 | readonly type: 'comment'; 9 | readonly value: string; 10 | }; 11 | 12 | export type TokenText = BaseToken & { 13 | readonly type: 'text'; 14 | readonly value: string; 15 | }; 16 | 17 | export type TokenData = BaseToken & { 18 | readonly type: 'data'; 19 | readonly escaped: boolean; 20 | readonly value: string; 21 | readonly filters?: ReadonlyArray<{ 22 | readonly name: string; 23 | readonly args: ReadonlyArray; 24 | }>; 25 | }; 26 | 27 | export type TokenFunction = BaseToken & { 28 | readonly type: 'function'; 29 | readonly name: string; 30 | readonly args?: ReadonlyArray; 31 | }; 32 | 33 | export type TokenRawFunction = BaseToken & { 34 | readonly type: 'raw-function'; 35 | readonly name: string; 36 | readonly args?: ReadonlyArray; 37 | readonly content: string; 38 | }; 39 | 40 | export type TokenEof = BaseToken & { 41 | readonly type: 'eof'; 42 | }; 43 | 44 | export type Token = 45 | | TokenComment 46 | | TokenText 47 | | TokenData 48 | | TokenFunction 49 | | TokenRawFunction 50 | | TokenEof 51 | ; 52 | 53 | export type FullToken = Location & Token; 54 | -------------------------------------------------------------------------------- /packages/bladejs-loader/README.md: -------------------------------------------------------------------------------- 1 | # bladejs 2 | [![TravisCI Build Status](https://travis-ci.org/Tarik02/laravel-blade-js.svg?branch=master)](https://travis-ci.org/Tarik02/laravel-blade-js) 3 | [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/f0an82rf5pdi4xl3/branch/master?svg=true)](https://ci.appveyor.com/project/Tarik02/laravel-blade-js/branch/master) 4 | [![npm version](https://badge.fury.io/js/%40tarik02%2Fbladejs-loader.svg)](https://badge.fury.io/js/%40tarik02%2Fbladejs-loader) 5 | 6 | ## Installation 7 | ```bash 8 | $ yarn add --dev @tarik02/bladejs-loader 9 | # or 10 | $ npm install --save-dev @tarik02/bladejs-loader 11 | ``` 12 | 13 | ## Usage 14 | Add as loader to `webpack.config.js`: 15 | ```javascript 16 | ... 17 | module: { 18 | rules: [ 19 | ... 20 | { 21 | test: /\.bjs$/, 22 | use: [ 23 | { 24 | loader: '@tarik02/bladejs-loader', 25 | options: { 26 | // use default blade functions and constructions (default: true) 27 | defaults: true, 28 | 29 | // list of custom plugins (paths to them) 30 | plugins: [ 31 | require.resolve('./plugins/SomePlugin'), 32 | ], 33 | }, 34 | }, 35 | ], 36 | }, 37 | ... 38 | ], 39 | }, 40 | ... 41 | ``` 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | - '10' 5 | - '12' 6 | env: 7 | matrix: 8 | - CURRENT_PACKAGE=bladejs 9 | - CURRENT_PACKAGE=bladejs-compiler 10 | - CURRENT_PACKAGE=bladejs-loader 11 | before_install: 12 | - cd packages/$CURRENT_PACKAGE 13 | install: 14 | - yarn install 15 | script: 16 | - node --version 17 | - yarn --version 18 | - yarn lint 19 | - yarn build 20 | - yarn test 21 | notifications: 22 | email: false 23 | deploy: 24 | skip_cleanup: true 25 | provider: npm 26 | email: Taras.Fomin@gmail.com 27 | api_key: 28 | secure: PaIPF2963OIwIdGAK0P81KisMATvHXneC9APPRe+NscunVF6+dfdu7Y3RHI3w4OvkQFlbcd8Lr7Be8T6FpviFaqygmObyRlQPknikEDKi6LWKDSOe9P61mIzvhSjStXoKcbOJ35V75ut8GxD3XQmbHOn1gGFyXGHfGwhRneAvo/JyPugxiYnrzIxRjhxzVlcCynnJoaF5+Lq9YOAMb/LZcYBbky8sbOjIf3LGVtpdXEfJZWDlkpDdufXrb4YUNKHlZHPXmnG3XVkfkfF7ZPbNGtU2mUx83iBAX9LeZmrT494FU6biVQEy+CpWHJQaMxR18g5oAHrjkzgRKCsb6q/ejpeWND0RrfcdP+p0tnYs1StXEegmlpzCjlHRWpY45S/2tHtSDX56i2E180GrwJQU23NI6xkINg9e07BW+oDnEwem+joZ02XrTZYMLA7ij/fdUldfcPyqZ9KVlK8+gERkf/hqsUf7+FQur5S/FO6aCrqbshxW3+8sv+OVIPp5KP8h20Z06Is/XfhOe6wDk5EJesc2SFT2D4v5a3Yym7WNdU21Qp83YLrFr9YNtKhs07RvuzbEbgqo3dc3qwCUxlXJXlmm1AEJZU7LN5qOzxrlWfwcdmYyBGbDJ521k+0OHU1EaMUJSyhTBOpjVQ1RTNnkrEd3ZFmYdtsZ3O58wbyRaM= 29 | on: 30 | tags: true 31 | branch: master 32 | condition: "$TRAVIS_NODE_VERSION = 12" 33 | repo: Tarik02/laravel-blade-js 34 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/README.md: -------------------------------------------------------------------------------- 1 | # bladejs-compiler 2 | [![TravisCI Build Status](https://travis-ci.org/Tarik02/laravel-blade-js.svg?branch=master)](https://travis-ci.org/Tarik02/laravel-blade-js) 3 | [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/f0an82rf5pdi4xl3/branch/master?svg=true)](https://ci.appveyor.com/project/Tarik02/laravel-blade-js/branch/master) 4 | [![npm version](https://badge.fury.io/js/%40tarik02%2Fbladejs-compiler.svg)](https://badge.fury.io/js/%40tarik02%2Fbladejs-compiler) 5 | 6 | ## Installation 7 | ```bash 8 | $ yarn add @tarik02/bladejs-compiler 9 | # or 10 | $ npm install --save @tarik02/bladejs-compiler 11 | ``` 12 | 13 | ## Usage 14 | ```typescript 15 | import { 16 | AbstractError, 17 | Compiler, 18 | Source, 19 | } from '@tarik02/bladejs-compiler'; 20 | 21 | const compiler = new Compiler(); 22 | compiler.addDefaults(); 23 | 24 | const source = ` 25 |
26 |
{{ title }}
27 | 28 | {{ slot }} 29 |
30 | `.trim(); 31 | const filename = 'test.bjs'; 32 | 33 | const source = new Source(source, filename); 34 | let compiled: string; 35 | 36 | try { 37 | compiled = compiler.compile(source); 38 | } catch (e) { 39 | if (e instanceof AbstractError) { 40 | process.stdout.write(e.prettyPrint()); 41 | process.exit(-1); 42 | } 43 | 44 | throw e; 45 | } 46 | ``` 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # laravel-blade-js 2 | [![TravisCI Build Status](https://travis-ci.org/Tarik02/laravel-blade-js.svg?branch=master)](https://travis-ci.org/Tarik02/laravel-blade-js) 3 | [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/f0an82rf5pdi4xl3/branch/master?svg=true)](https://ci.appveyor.com/project/Tarik02/laravel-blade-js/branch/master) 4 | [![npm version](https://badge.fury.io/js/%40tarik02%2Fbladejs.svg)](https://badge.fury.io/js/%40tarik02%2Fbladejs) 5 | 6 | ## Packages 7 | The project is divided into two separate packages: 8 | - compiler - package that can compile blade templates into pure JavaScript async generator functions. 9 | - runtime - package that can run these functions. 10 | - loader - webpack loader. 11 | 12 | Compiler can be used together with runtime, but it is recommended to compile templates before using them in server or browser. 13 | 14 | ## Installation and Usage 15 | - [runtime](https://github.com/Tarik02/laravel-blade-js/tree/master/packages/bladejs) 16 | - [compiler](https://github.com/Tarik02/laravel-blade-js/tree/master/packages/bladejs-compiler) 17 | - [loader](https://github.com/Tarik02/laravel-blade-js/tree/master/packages/bladejs-loader) 18 | 19 | ## Examples 20 | - [express](https://github.com/Tarik02/laravel-blade-js/tree/master/examples/express) 21 | - [webpack loader](https://github.com/Tarik02/laravel-blade-js/tree/master/examples/loader) 22 | 23 | -------------------------------------------------------------------------------- /packages/bladejs/README.md: -------------------------------------------------------------------------------- 1 | # bladejs 2 | [![TravisCI Build Status](https://travis-ci.org/Tarik02/laravel-blade-js.svg?branch=master)](https://travis-ci.org/Tarik02/laravel-blade-js) 3 | [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/f0an82rf5pdi4xl3/branch/master?svg=true)](https://ci.appveyor.com/project/Tarik02/laravel-blade-js/branch/master) 4 | [![npm version](https://badge.fury.io/js/%40tarik02%2Fbladejs.svg)](https://badge.fury.io/js/%40tarik02%2Fbladejs) 5 | 6 | ## Installation 7 | ```bash 8 | $ yarn add @tarik02/bladejs 9 | # or 10 | $ npm install --save @tarik02/bladejs 11 | ``` 12 | 13 | ## Usage 14 | ```typescript 15 | import { 16 | CompiledTemplate, 17 | Runtime, 18 | TemplateProvider, 19 | } from '@tarik02/bladejs'; 20 | 21 | const templateProvider: TemplateProvider = { 22 | async getTemplateCompiledFile(name: string): Promise { 23 | // TODO: Load template, return undefined if does not exist 24 | }, 25 | 26 | async isOutdated(name: string, template: CompiledTemplate, creationTime: number): Promise { 27 | // TODO: Return true if template is outdated (this will cause to {getTemplateCompiledFile} call) 28 | }, 29 | }; 30 | 31 | const runtime = new Runtime([templateProvider]); 32 | 33 | (async () => { 34 | for await (const chunk of runtime.render('test')) { 35 | process.stdout.write(chunk); 36 | } 37 | })(); 38 | ``` 39 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/cli/bladejs-compiler.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs-extra'; 2 | import { inspect } from 'util'; 3 | import { AbstractError, Compiler, Source } from '..'; 4 | import { Output } from './Output'; 5 | 6 | export default async function(exe: string, args: string[], out: Output): Promise { 7 | if (args.length !== 2) { 8 | await out.write(`Usage:\n`); 9 | await out.write(` ${exe} \n`); 10 | return -1; 11 | } 12 | 13 | const [input, output] = args; 14 | 15 | await out.write(`Reading ${inspect(input)}...\n`); 16 | 17 | let source: Source; 18 | try { 19 | source = new Source(await fs.readFile(input, 'utf-8'), input); 20 | } catch (e) { 21 | if ('code' in e && e.code === 'ENOENT') { 22 | await out.write(`File ${inspect(input)} does not exist\n`); 23 | return -1; 24 | } 25 | 26 | throw e; 27 | } 28 | 29 | const compiler = new Compiler(); 30 | compiler.addDefaults(); 31 | 32 | await out.write(`Compiling...\n`); 33 | 34 | let compiled: string; 35 | try { 36 | compiled = compiler.compile(source); 37 | } catch (e) { 38 | if (e instanceof AbstractError) { 39 | await out.write(e.prettyPrint(true)); 40 | return -1; 41 | } 42 | 43 | throw e; 44 | } 45 | 46 | await out.write(`Saving ${inspect(output)}...\n`); 47 | 48 | try { 49 | await fs.writeFile(output, compiled); 50 | } catch (e) { 51 | throw e; 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /packages/bladejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tarik02/bladejs", 3 | "version": "3.0.0", 4 | "license": "MIT", 5 | "author": { 6 | "name": "Tarik02", 7 | "url": "https://github.com/Tarik02" 8 | }, 9 | "description": "Laravel's blade templating engine for JavaScript and TypeScript.", 10 | "keywords": [ 11 | "template", 12 | "html", 13 | "laravel", 14 | "blade" 15 | ], 16 | "main": "lib/index.js", 17 | "types": "lib/index.d.ts", 18 | "scripts": { 19 | "build": "tsc -p tsconfig.json", 20 | "lint": "tslint -c ../../tslint.json 'src/**/*.ts' 'test/**/*.ts'", 21 | "lint:fix": "yarn lint --fix", 22 | "test": "mocha -r ts-node/register test/**/*.ts", 23 | "prepublishOnly": "yarn build && yarn lint && yarn test" 24 | }, 25 | "homepage": "https://github.com/Tarik02/laravel-blade-js#readme", 26 | "bugs": { 27 | "url": "https://github.com/Tarik02/laravel-blade-js#readme" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/Tarik02/laravel-blade-js.git" 32 | }, 33 | "publishConfig": { 34 | "access": "public" 35 | }, 36 | "dependencies": { 37 | "tslib": "^1.10.0" 38 | }, 39 | "devDependencies": { 40 | "@types/chai": "^4.1.7", 41 | "@types/mocha": "^5.2.7", 42 | "@types/node": "^12.6.3", 43 | "chai": "^4.2.0", 44 | "chai-exclude": "^2.0.1", 45 | "mocha": "^6.1.4", 46 | "ts-node": "^8.3.0", 47 | "tslint": "^5.18.0", 48 | "typescript": "^3.5.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/bladejs-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tarik02/bladejs-loader", 3 | "version": "3.0.0", 4 | "license": "MIT", 5 | "author": { 6 | "name": "Tarik02", 7 | "url": "https://github.com/Tarik02" 8 | }, 9 | "description": "Laravel's blade templating engine for JavaScript and TypeScript.", 10 | "keywords": [ 11 | "template", 12 | "html", 13 | "laravel", 14 | "blade" 15 | ], 16 | "main": "lib/index.js", 17 | "types": "lib/index.d.ts", 18 | "scripts": { 19 | "build": "tsc -p tsconfig.json", 20 | "lint": "tslint -c ../../tslint.json 'src/**/*.ts' 'test/**/*.ts'", 21 | "lint:fix": "yarn lint --fix", 22 | "test": "echo ''", 23 | "prepublishOnly": "yarn build && yarn lint && yarn test" 24 | }, 25 | "homepage": "https://github.com/Tarik02/laravel-blade-js#readme", 26 | "bugs": { 27 | "url": "https://github.com/Tarik02/laravel-blade-js#readme" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/Tarik02/laravel-blade-js.git" 32 | }, 33 | "publishConfig": { 34 | "access": "public" 35 | }, 36 | "dependencies": { 37 | "loader-utils": "^1.2.3", 38 | "schema-utils": "^1.0.0", 39 | "tslib": "^1.10.0" 40 | }, 41 | "devDependencies": { 42 | "@types/loader-utils": "^1.1.3", 43 | "@types/node": "^12.6.3", 44 | "@types/schema-utils": "^1.0.0", 45 | "ts-node": "^8.3.0", 46 | "tslint": "^5.18.0", 47 | "typescript": "^3.5.3" 48 | }, 49 | "peerDependencies": { 50 | "@tarik02/bladejs-compiler": "^3.0.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/StacksCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | import { CompilerPlugin } from './CompilerPlugin'; 3 | 4 | export class StacksCompilerPlugin implements CompilerPlugin { 5 | public init(compiler: Compiler): void { 6 | compiler.addSequence('push', [ 7 | { name: 'endpush', required: true, multiple: false }, 8 | ], (delegate, node) => { 9 | const [prefix, suffix] = node.data[0]; 10 | delegate.expectedArgs(prefix, 1); 11 | delegate.expectedArgs(node.ending, 0); 12 | 13 | delegate.append('__env.push('); 14 | delegate.append(prefix.args![0]); 15 | delegate.append(', async function *() {'); 16 | delegate.compileContainer(suffix); 17 | delegate.append('}, false);'); 18 | }); 19 | 20 | compiler.addSequence('prepend', [ 21 | { name: 'endprepend', required: true, multiple: false }, 22 | ], (delegate, node) => { 23 | const [prefix, suffix] = node.data[0]; 24 | delegate.expectedArgs(prefix, 1); 25 | delegate.expectedArgs(node.ending, 0); 26 | 27 | delegate.append('__env.push('); 28 | delegate.append(prefix.args![0]); 29 | delegate.append(', async function *() {'); 30 | delegate.compileContainer(suffix); 31 | delegate.append('}, true);'); 32 | }); 33 | 34 | compiler.addFunction('stack', (delegate, node) => { 35 | delegate.expectedArgs(node, 1); 36 | 37 | delegate.append('yield* __env.stack('); 38 | delegate.append(node.args![0]); 39 | delegate.append(');'); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/bladejs/src/environment/Environment.ts: -------------------------------------------------------------------------------- 1 | import { Runtime } from '../runtime/Runtime'; 2 | 3 | export type TemplateParams = { 4 | [key: string]: any; 5 | }; 6 | 7 | export interface EnvironmentLoop { 8 | __hasRemaining: boolean; 9 | __key: any; 10 | __value: any; 11 | __next(): void; 12 | 13 | iteration: number; 14 | index: number; 15 | remaining: number; 16 | count: number; 17 | first: boolean; 18 | last: boolean; 19 | odd: boolean; 20 | even: boolean; 21 | depth: number; 22 | parent?: EnvironmentLoop; 23 | } 24 | 25 | export interface Environment { 26 | readonly runtime: Runtime; 27 | readonly params: TemplateParams; 28 | 29 | process(input: AsyncIterable): AsyncIterable; 30 | 31 | print(text: any, escaped: boolean): AsyncIterable; 32 | 33 | filter(text: any, name: string, ...args: any[]): PromiseLike | string; 34 | 35 | call(name: string, ...args: any[]): AsyncIterable; 36 | 37 | extends(parent: string): AsyncIterable; 38 | 39 | section( 40 | name: string, 41 | renderer: ((parent: AsyncIterable) => AsyncIterable) | any, 42 | isShow: boolean, 43 | ): void | AsyncIterable; 44 | 45 | yield(name: string, def?: string): string | string[] | AsyncIterable; 46 | 47 | pushLoop(iteratee: any): EnvironmentLoop; 48 | popLoop(): void; 49 | 50 | push(name: string, renderer: () => AsyncIterable, prepend?: boolean): void; 51 | stack(name: string): AsyncIterable; 52 | 53 | beginComponent(name: string, args?: TemplateParams): void; 54 | endComponent(): AsyncIterable; 55 | 56 | beginSlot(name: string): void; 57 | endSlot(): void; 58 | } 59 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tarik02/bladejs-compiler", 3 | "version": "3.0.0", 4 | "license": "MIT", 5 | "author": { 6 | "name": "Tarik02", 7 | "url": "https://github.com/Tarik02" 8 | }, 9 | "description": "Laravel's blade templating engine for JavaScript and TypeScript.", 10 | "keywords": [ 11 | "template", 12 | "html", 13 | "laravel", 14 | "blade" 15 | ], 16 | "main": "lib/index.js", 17 | "types": "lib/index.d.ts", 18 | "scripts": { 19 | "build": "tsc -p tsconfig.json", 20 | "lint": "tslint -c ../../tslint.json 'src/**/*.ts' 'test/**/*.ts'", 21 | "lint:fix": "yarn lint --fix", 22 | "test": "mocha -r ts-node/register test/**/*.ts", 23 | "prepublishOnly": "yarn build && yarn lint && yarn test" 24 | }, 25 | "bin": { 26 | "bladejs-c": "./bin/bladejs-c", 27 | "bladejs-compiler": "./bin/bladejs-compiler" 28 | }, 29 | "homepage": "https://github.com/Tarik02/laravel-blade-js#readme", 30 | "bugs": { 31 | "url": "https://github.com/Tarik02/laravel-blade-js#readme" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/Tarik02/laravel-blade-js.git" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "dependencies": { 41 | "ansi-colors": "^4.1.1", 42 | "fs-extra": "^8.1.0", 43 | "tslib": "^1.10.0", 44 | "vue-template-es2015-compiler": "^1.9.1" 45 | }, 46 | "devDependencies": { 47 | "@types/chai": "^4.1.7", 48 | "@types/fs-extra": "^8.0.0", 49 | "@types/mocha": "^5.2.7", 50 | "@types/node": "^12.6.2", 51 | "chai": "^4.2.0", 52 | "chai-exclude": "^2.0.1", 53 | "mocha": "^6.1.4", 54 | "ts-node": "^8.3.0", 55 | "tslint": "^5.18.0", 56 | "typescript": "^3.5.3" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/util.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, use } from 'chai'; 2 | import chaiExclude from 'chai-exclude'; 3 | 4 | use(chaiExclude); 5 | 6 | import { LexerError } from '../src/error/LexerError'; 7 | import { createLexer, LexerConfig } from '../src/lexer/Lexer'; 8 | import { Token } from '../src/lexer/Token'; 9 | import { NodeRecursiveWithoutLocation } from '../src/parser/Node'; 10 | import { parse, ParserConfig } from '../src/parser/Parser'; 11 | import { Scanner } from '../src/scanner/Scanner'; 12 | import { Source } from '../src/source/Source'; 13 | 14 | export const createLexerFromText = 15 | (text: string, config?: Partial) => createLexer(new Scanner(new Source(text)), config) 16 | ; 17 | 18 | export const lex = (text: string, config?: Partial): Token[] => { 19 | const lexer = createLexerFromText(text, config); 20 | let tok: Token; 21 | 22 | const result = []; 23 | while ((tok = lexer.next()).type !== 'eof') { 24 | result.push(tok); 25 | } 26 | return result; 27 | }; 28 | 29 | export const lexAssert = (text: string, expectedTokens: Token[], config?: Partial) => { 30 | const actualTokens = lex(text, config); 31 | 32 | expect(actualTokens).excludingEvery(['start', 'end']).deep.eq(expectedTokens); 33 | }; 34 | 35 | export const lexError = (text: string, config?: Partial): LexerError => { 36 | try { 37 | lex(text, config); 38 | } catch (e) { 39 | if (e instanceof LexerError) { 40 | return e; 41 | } 42 | } 43 | 44 | expect.fail(undefined, undefined, 'lex expected to throw an error'); 45 | throw new Error('unreachable'); 46 | }; 47 | 48 | export const parseAssert = ( 49 | text: string, 50 | expected: NodeRecursiveWithoutLocation, 51 | parserConfig?: Partial, 52 | lexerConfig?: Partial, 53 | ) => { 54 | const actual = parse(createLexerFromText(text, lexerConfig), parserConfig); 55 | 56 | expect(actual).excludingEvery(['start', 'end']).deep.eq(expected); 57 | }; 58 | -------------------------------------------------------------------------------- /packages/bladejs/test/util/trimAsyncIterable.test.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-expression */ 2 | 3 | import { expect } from 'chai'; 4 | import 'mocha'; 5 | import { trimAsyncIterable } from '../../src/util/trimAsyncIterable'; 6 | 7 | const generate = async function *(chunks: string[]): AsyncIterable { 8 | for (const chunk of chunks) { 9 | yield chunk; 10 | } 11 | }; 12 | 13 | const collect = async (iteratee: AsyncIterable): Promise => { 14 | let result = ''; 15 | for await (const chunk of iteratee) { 16 | result += chunk; 17 | } 18 | return result; 19 | }; 20 | 21 | const testTrim = async (chunks: string[]): Promise => { 22 | return await collect(trimAsyncIterable(generate(chunks))); 23 | }; 24 | 25 | describe('runtime/trimAsyncIterable', () => { 26 | it('should trim single string', async () => { 27 | expect(await testTrim([ 28 | ' Hello World ', 29 | ])).eq('Hello World'); 30 | }); 31 | 32 | it('should trim two strings', async () => { 33 | expect(await testTrim([ 34 | ' Hello ', 35 | ' World ', 36 | ])).eq('Hello World'); 37 | }); 38 | 39 | it('should trim many strings', async () => { 40 | expect(await testTrim([ 41 | ' Hel', 42 | 'lo ', 43 | 'Wor', 44 | 'ld ', 45 | ])).eq('Hello World'); 46 | }); 47 | 48 | it('should trim empty sequence', async () => { 49 | expect(await testTrim([])).eq(''); 50 | }); 51 | 52 | it('should trim empty string', async () => { 53 | expect(await testTrim([''])).eq(''); 54 | }); 55 | 56 | it('should not trim single trimmed string', async () => { 57 | expect(await testTrim([ 58 | 'Hello World', 59 | ])).eq('Hello World'); 60 | }); 61 | 62 | it('should not trim two trimmed strings', async () => { 63 | expect(await testTrim([ 64 | 'Hello ', 65 | ' World', 66 | ])).eq('Hello World'); 67 | }); 68 | 69 | it('should not trim many trimmed strings', async () => { 70 | expect(await testTrim([ 71 | 'Hel', 72 | 'lo', 73 | ' ', 74 | 'Wo', 75 | 'rld', 76 | '', 77 | ])).eq('Hello World'); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /packages/bladejs-loader/src/index.ts: -------------------------------------------------------------------------------- 1 | // This is a peer dependency 2 | // @ts-ignore 3 | import { AbstractError, Compiler, Source } from '@tarik02/bladejs-compiler'; 4 | import { getOptions } from 'loader-utils'; 5 | import * as path from 'path'; 6 | import validateOptions = require('schema-utils'); 7 | import { inspect } from 'util'; 8 | import * as webpack from 'webpack'; 9 | 10 | const schema = { 11 | type: 'object', 12 | properties: { 13 | defaults: { 14 | type: 'boolean', 15 | }, 16 | plugins: { 17 | type: 'array', 18 | items: { 19 | type: 'string', 20 | }, 21 | }, 22 | }, 23 | }; 24 | 25 | const loader: webpack.loader.Loader = function (this: webpack.loader.LoaderContext, source: string | Buffer) { 26 | this.cacheable(true); 27 | 28 | const options: any = getOptions(this) || {}; 29 | 30 | validateOptions(schema, options, 'bladejs-loader'); 31 | 32 | if (source instanceof Buffer) { 33 | source = source.toString('utf-8'); 34 | } 35 | 36 | const compiler = new Compiler(); 37 | 38 | if (!('defaults' in options) || options.defaults === true) { 39 | compiler.addDefaults(); 40 | } 41 | 42 | if ('plugins' in options) { 43 | for (const plugin of options.plugins) { 44 | const pluginCons = require(plugin); 45 | const basename = path.parse(plugin).name; 46 | 47 | if (typeof pluginCons === 'function') { 48 | compiler.addPlugin(pluginCons); 49 | } else if (typeof pluginCons.default === 'function') { 50 | compiler.addPlugin(pluginCons.default); 51 | } else if (typeof pluginCons[basename] === 'function') { 52 | compiler.addPlugin(pluginCons[basename]); 53 | } else { 54 | this.emitError(`Cannot include plugin ${inspect(plugin)}: it should export function (constructor)`); 55 | } 56 | } 57 | } 58 | 59 | try { 60 | const compiled = compiler.compile(new Source(source, this.resourcePath)); 61 | return `export default ${compiled}`; 62 | } catch (e) { 63 | if (e instanceof AbstractError) { 64 | this.emitError(e.prettyPrint(true)); 65 | } 66 | 67 | return; 68 | } 69 | }; 70 | 71 | export default loader; 72 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/scanner/Scanner.ts: -------------------------------------------------------------------------------- 1 | import { LexerError } from '../error/LexerError'; 2 | import { Position } from '../source/Position'; 3 | import { Source } from '../source/Source'; 4 | 5 | export class Scanner { 6 | public readonly source: Source; 7 | private readonly input: string; 8 | 9 | private offset: number = 0; 10 | private line: number = 1; 11 | private column: number = 1; 12 | private cachedPosition: Position | undefined; 13 | 14 | public get position(): Position { 15 | if (this.cachedPosition === undefined) { 16 | this.cachedPosition = new Position(this.source, this.offset, this.line, this.column); 17 | } 18 | return this.cachedPosition; 19 | } 20 | 21 | public constructor(source: Source) { 22 | this.source = source; 23 | this.input = source.text; 24 | } 25 | 26 | public peek(offset: number = 0): string | undefined { 27 | return this.input[this.offset + offset]; 28 | } 29 | 30 | public sub(length: number, offset: number = 0): string { 31 | return this.input.substring(this.offset + offset, this.offset + offset + length); 32 | } 33 | 34 | public next(throwEof: boolean = true): string { 35 | if (this.offset >= this.input.length) { 36 | if (throwEof) { 37 | throw new LexerError(this.source, { 38 | start: this.position, 39 | end: this.position, 40 | }, 'unexpected end of file'); 41 | } 42 | 43 | return ''; 44 | } 45 | 46 | this.cachedPosition = undefined; 47 | const ch = this.input[this.offset++]; 48 | if (ch === '\n') { 49 | ++this.line; 50 | this.column = 1; 51 | } else { 52 | ++this.column; 53 | } 54 | return ch; 55 | } 56 | 57 | public skip(count: number = 1): void { 58 | this.cachedPosition = undefined; 59 | 60 | while (--count >= 0) { 61 | if (this.input[this.offset++] === '\n') { 62 | ++this.line; 63 | this.column = 1; 64 | } else { 65 | ++this.column; 66 | } 67 | } 68 | } 69 | 70 | public eof(): boolean { 71 | return this.offset >= this.input.length; 72 | } 73 | 74 | public eol(): boolean { 75 | return this.peek() === '\n'; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/source/Position.ts: -------------------------------------------------------------------------------- 1 | import {Source} from './Source'; 2 | 3 | export class Position { 4 | public readonly source: Source; 5 | public readonly offset: number; 6 | 7 | private cachedLine: number | undefined; 8 | private cachedColumn: number | undefined; 9 | 10 | /** 11 | * Return line number of position (starting from 1) 12 | */ 13 | public get line(): number { 14 | if (this.cachedLine === undefined) { 15 | this.cacheLineAndColumn(); 16 | } 17 | return this.cachedLine!; 18 | } 19 | 20 | /** 21 | * Return column number of position(starts from 0) 22 | */ 23 | get column(): number { 24 | if (this.cachedColumn === undefined) { 25 | this.cacheLineAndColumn(); 26 | } 27 | return this.cachedColumn!; 28 | } 29 | 30 | public constructor( 31 | source: Source, 32 | offset: number, 33 | line?: number, 34 | column?: number) { 35 | this.source = source; 36 | this.offset = offset; 37 | this.cachedLine = line; 38 | this.cachedColumn = column; 39 | } 40 | 41 | public relative(offset: number): Position { 42 | if (offset === 0) { 43 | return this; 44 | } 45 | 46 | // TODO: Put line and column? 47 | return new Position(this.source, this.offset + offset); 48 | } 49 | 50 | public until(other: Position): string { 51 | if (other.source !== this.source) { 52 | throw new Error('Positions are with different code'); 53 | } 54 | 55 | const [a, b]: number[] = [this.offset, other.offset].sort(); 56 | return this.source.text.substring(a, b); 57 | } 58 | 59 | public equals(other: Position): boolean { 60 | return this.source === other.source && this.offset === other.offset; 61 | } 62 | 63 | public toString(): string { 64 | return `${this.line}:${this.column}`; 65 | } 66 | 67 | private cacheLineAndColumn() { 68 | this.cachedLine = 1; 69 | this.cachedColumn = 1; 70 | 71 | const text = this.source.text; 72 | for (let i: number = 0; i < this.offset; ++i) { 73 | if (text[i] === '\n') { 74 | ++this.cachedLine; 75 | this.cachedColumn = 1; 76 | } else { 77 | ++this.cachedColumn; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/express/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as express from 'express'; 2 | import { Response } from 'express-serve-static-core'; 3 | import * as fs from 'fs-extra'; 4 | import * as path from 'path'; 5 | import { performance } from 'perf_hooks'; 6 | import { CompiledTemplate, Runtime, TemplateParams, TemplateProvider } from '../../../packages/bladejs'; 7 | import { AbstractError, Compiler, Source } from '../../../packages/bladejs-compiler'; 8 | 9 | const app = express(); 10 | 11 | const viewsDir = path.join(__dirname, '../views'); 12 | const viewsExt = 'bjs'; 13 | const getTemplateFileName = (name: string) => path.join(viewsDir, `${name.replace('.', '/')}.${viewsExt}`); 14 | 15 | const viewsCompiler = new Compiler(); 16 | viewsCompiler.addDefaults(); 17 | 18 | const templateProvider: TemplateProvider = { 19 | async getTemplateCompiledFile(name: string): Promise { 20 | try { 21 | const source = new Source( 22 | await fs.readFile(getTemplateFileName(name), 'utf-8'), 23 | `${name}.${viewsExt}`, 24 | ); 25 | const compiled = viewsCompiler.compile(source); 26 | 27 | return (new Function('__env', 'return ' + compiled + '(__env)')) as CompiledTemplate; 28 | } catch (e) { 29 | if (e instanceof AbstractError) { 30 | process.stdout.write(e.prettyPrint()); 31 | } else if ('code' in e && e.code === 'ENOENT') { 32 | console.error(`Template ${name} does not exist`); 33 | } else { 34 | console.error(e); 35 | } 36 | } 37 | 38 | return undefined; 39 | }, 40 | 41 | async isOutdated(name: string, _template: CompiledTemplate, creationTime: number): Promise { 42 | const stat = await fs.stat(getTemplateFileName(name)); 43 | return stat.mtimeMs > creationTime; 44 | }, 45 | }; 46 | 47 | const views = new Runtime([templateProvider]); 48 | 49 | const writeView = async (res: Response, name: string, params?: TemplateParams): Promise => { 50 | const start = performance.now(); 51 | for await (const chunk of views.render(name, params)) { 52 | res.write(chunk); 53 | } 54 | const end = performance.now(); 55 | 56 | res.write(`\n\n\n`); 57 | }; 58 | 59 | app.get('/', async (_req, res) => { 60 | await writeView(res, 'index', { 61 | myVar: 'My variable value', 62 | }); 63 | res.send(); 64 | }); 65 | 66 | app.listen(3000, () => { 67 | console.log('Example app listening on port 3000!'); 68 | }); 69 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/ComponentsCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | import { isNotEmptyContainer } from '../parser/Node'; 3 | import { CompilerPlugin } from './CompilerPlugin'; 4 | 5 | export class ComponentsCompilerPlugin implements CompilerPlugin { 6 | public init(compiler: Compiler): void { 7 | compiler.addSequence('component', [ 8 | { name: ['slot', 'endslot'], required: false, multiple: true }, 9 | { name: 'endcomponent', required: true, multiple: false }, 10 | ], (delegate, node) => { 11 | delegate.expectedArgs(node.ending, 0); 12 | 13 | let isSlot = false; 14 | for (const [prefix, suffix] of node.data) { 15 | switch (prefix.name) { 16 | case 'component': 17 | delegate.expectedArgs(prefix, 1, 2); 18 | delegate.append('__env.beginComponent('); 19 | delegate.append(prefix.args!.join(', ')); 20 | delegate.append(');\n'); 21 | break; 22 | case 'slot': 23 | if (isSlot) { 24 | return delegate.error('expected @endslot before @slot', prefix, { 25 | start: prefix.start, 26 | end: prefix.start.relative(1 + prefix.name.length), 27 | }); 28 | } 29 | isSlot = true; 30 | 31 | delegate.expectedArgs(prefix, 1); 32 | delegate.append('__env.beginSlot('); 33 | delegate.append(prefix.args![0]); 34 | delegate.append(');\n'); 35 | break; 36 | case 'endslot': 37 | if (!isSlot) { 38 | return delegate.error('expected @slot before @endslot', prefix, { 39 | start: prefix.start, 40 | end: prefix.start.relative(1 + prefix.name.length), 41 | }); 42 | } 43 | isSlot = false; 44 | 45 | delegate.expectedArgs(prefix, 0); 46 | delegate.append('__env.endSlot();\n'); 47 | break; 48 | } 49 | 50 | if (isNotEmptyContainer(suffix)) { 51 | delegate.compileContainer(suffix); 52 | } 53 | } 54 | 55 | delegate.append('yield* __env.endComponent();'); 56 | return undefined; 57 | }); 58 | 59 | compiler.addSequence('slot', [ 60 | { name: 'endslot', required: true, multiple: false }, 61 | ], (delegate, node) => { 62 | const [prefix, suffix] = node.data[0]; 63 | delegate.expectedArgs(prefix, 0, 1); 64 | delegate.expectedArgs(node.ending, 0); 65 | 66 | delegate.append('__env.beginSlot('); 67 | delegate.append(prefix.args![0]); 68 | delegate.append(');\n'); 69 | delegate.compileContainer(suffix); 70 | delegate.append('__env.endSlot();\n'); 71 | }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/error/AbstractError.ts: -------------------------------------------------------------------------------- 1 | import * as colors from 'ansi-colors'; 2 | import { Location } from '../source/Location'; 3 | import { Source } from '../source/Source'; 4 | 5 | export abstract class AbstractError extends Error { 6 | public readonly source: Source; 7 | public readonly position: Location; 8 | 9 | public constructor(source: Source, position: Location, message?: string) { 10 | super(message); 11 | 12 | this.source = source; 13 | this.position = position; 14 | 15 | Object.setPrototypeOf(this, new.target.prototype); 16 | this.name = this.constructor.name; 17 | } 18 | 19 | public prettyPrint(withColors: boolean = true): string { 20 | const oldEnabled = colors.enabled; 21 | // @ts-ignore 22 | colors.enabled = withColors; 23 | 24 | try { 25 | const { position: { start, end }, message, source } = this; 26 | 27 | let result = ''; 28 | 29 | result += `${source.file || 'unknown'}:${start}: ${message}\n`; 30 | 31 | const pad = `${end.line + 1}. `.length; 32 | for (let i = start.line - 1; i <= end.line + 1; ++i) { 33 | const line = source.getLine(i); 34 | if (line !== undefined) { 35 | const linePrefix = `${i}. `.padEnd(pad); 36 | const printedLine = linePrefix + ( 37 | i === start.line 38 | ? line.substr(0, start.column - 1) + colors.red(line.substr(start.column - 1)) 39 | : i === end.line 40 | ? colors.red(line.substr(0, end.column - 1)) + line.substr(end.column - 1) 41 | : i >= start.line && i <= end.line 42 | ? colors.red(line) 43 | : line 44 | ); 45 | 46 | if (i === start.line) { 47 | if (start.line === end.line) { 48 | result += printedLine + '\n'; 49 | result += ' '.repeat(pad + start.column - 1); 50 | result += colors.red('^' + '~'.repeat(Math.max(0, end.column - pad - start.column + 2))); 51 | result += '\n'; 52 | } else { 53 | result += ' '.repeat(pad + start.column - 1); 54 | result += colors.red(colors.bold('∨') + '~'.repeat(line.length - start.column + 1)); 55 | result += '\n' + printedLine + '\n'; 56 | } 57 | } else if (start.line !== end.line && i === end.line) { 58 | result += printedLine + '\n'; 59 | result += colors.red('~'.repeat(pad + end.column - 2) + '^'); 60 | result += '\n'; 61 | } else { 62 | result += printedLine + '\n'; 63 | } 64 | } 65 | } 66 | 67 | return result; 68 | } finally { 69 | // @ts-ignore 70 | colors.enabled = oldEnabled; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/LayoutCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | import { CompilerPlugin } from './CompilerPlugin'; 3 | 4 | export class LayoutCompilerPlugin implements CompilerPlugin { 5 | public init(compiler: Compiler): void { 6 | compiler.addFunction('extends', (delegate, node) => { 7 | delegate.expectedArgs(node, 1); 8 | 9 | delegate.footer.append('yield* __env.extends('); 10 | delegate.footer.append(node.args!.join(', ')); 11 | delegate.footer.append(');\n'); 12 | }); 13 | 14 | compiler.addSequence('section', (args?: ReadonlyArray) => ( 15 | !args || args.length === 1 16 | ? [ 17 | { name: 'parent', required: false, multiple: false }, 18 | { name: ['show', 'endsection'], required: true, multiple: false }, 19 | ] 20 | : [] 21 | ), (delegate, node) => { 22 | if (node.data.length === 0) { 23 | const ending = node.ending; 24 | delegate.expectedArgs(ending, 2); 25 | 26 | delegate.append('yield* '); 27 | delegate.append('__env.section('); 28 | delegate.append(ending.args!.join(', ')); 29 | delegate.append(');'); 30 | } else { 31 | const isShow = node.ending.name === 'show'; 32 | const starting = node.data[0][0]; 33 | delegate.expectedArgs(starting, 1); 34 | 35 | // if (isShow) { 36 | delegate.append('yield* '); 37 | // } 38 | delegate.append('__env.section('); 39 | delegate.append(starting.args![0]); 40 | delegate.append(', async function *(__parent) {\n'); 41 | for (const [prefix, suffix] of node.data) { 42 | if (prefix.name === 'parent') { 43 | delegate.append('yield* __parent;\n'); 44 | } 45 | delegate.compileContainer(suffix); 46 | } 47 | delegate.append('}, '); 48 | delegate.append(JSON.stringify(isShow)); 49 | delegate.append(');'); 50 | } 51 | }); 52 | 53 | compiler.addSequence('hasSection', [ 54 | { name: 'else', required: false, multiple: false }, 55 | { name: 'endif', required: true, multiple: false }, 56 | ], (delegate, node) => { 57 | for (const [prefix, suffix] of node.data) { 58 | switch (prefix.name) { 59 | case 'hasSection': 60 | delegate.expectedArgs(prefix, 1); 61 | delegate.append('if (__env.hasSection('); 62 | delegate.append(prefix.args![0]); 63 | delegate.append(')) {\n'); 64 | break; 65 | case 'else': 66 | delegate.expectedArgs(prefix, 0); 67 | delegate.append('} else {\n'); 68 | break; 69 | } 70 | 71 | delegate.compileNode(suffix); 72 | } 73 | 74 | delegate.expectedArgs(node.ending, 0); 75 | delegate.append('}'); 76 | }); 77 | 78 | compiler.addFunction('yield', (delegate, node) => { 79 | delegate.expectedArgs(node, 1, 2); 80 | 81 | delegate.append('yield* __env.yield('); 82 | delegate.append(node.args!.join(', ')); 83 | delegate.append(');'); 84 | }); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/parser/Node.ts: -------------------------------------------------------------------------------- 1 | import { Location } from '../source/Location'; 2 | 3 | export type BaseNode = Location & { 4 | readonly type: string; 5 | }; 6 | 7 | export type NodeContainer = BaseNode & { 8 | readonly type: 'container'; 9 | readonly children: ReadonlyArray; 10 | }; 11 | 12 | export type NodeComment = BaseNode & { 13 | readonly type: 'comment'; 14 | readonly value: string; 15 | }; 16 | 17 | export type NodeText = BaseNode & { 18 | readonly type: 'text'; 19 | readonly value: string; 20 | }; 21 | 22 | export type NodeData = BaseNode & { 23 | readonly type: 'data'; 24 | readonly escaped: boolean; 25 | readonly value: string; 26 | readonly filters?: ReadonlyArray<{ 27 | readonly name: string; 28 | readonly args: ReadonlyArray; 29 | }>; 30 | }; 31 | 32 | export type BaseFunction = BaseNode & { 33 | readonly name: string; 34 | readonly args?: ReadonlyArray; 35 | }; 36 | 37 | export type NodeFunction = BaseFunction & { 38 | readonly type: 'function'; 39 | }; 40 | 41 | export type NodeRawFunction = BaseFunction & { 42 | readonly type: 'raw-function'; 43 | readonly content: string; 44 | }; 45 | 46 | export type NodeSequence = BaseNode & { 47 | readonly type: 'sequence'; 48 | 49 | readonly data: ReadonlyArray<[NodeFunction, NodeContainer]>; 50 | readonly ending: NodeFunction; 51 | }; 52 | 53 | export type Node = 54 | | NodeContainer 55 | | NodeComment 56 | | NodeText 57 | | NodeData 58 | | NodeFunction 59 | | NodeRawFunction 60 | | NodeSequence 61 | ; 62 | 63 | // node types without location 64 | export type NodeWithoutLocation = 65 | | Omit 66 | | Omit 67 | | Omit 68 | | Omit 69 | | Omit 70 | | Omit 71 | | Omit 72 | ; 73 | 74 | // recursive node types without position (start, end) 75 | 76 | export type NodeContainerRWL = Omit & { 77 | readonly children: ReadonlyArray; 78 | }; 79 | 80 | export type NodeCommentRWL = Omit; 81 | 82 | export type NodeTextRWL = Omit; 83 | 84 | export type NodeDataRWL = Omit; 85 | 86 | export type NodeFunctionRWL = Omit; 87 | 88 | export type NodeRawFunctionRWL = Omit; 89 | 90 | export type NodeSequenceRWL = Omit & { 91 | readonly data: ReadonlyArray<[NodeFunctionRWL, NodeContainerRWL]>; 92 | readonly ending: NodeFunctionRWL; 93 | }; 94 | 95 | export type NodeRecursiveWithoutLocation = 96 | | NodeContainerRWL 97 | | NodeCommentRWL 98 | | NodeTextRWL 99 | | NodeDataRWL 100 | | NodeFunctionRWL 101 | | NodeRawFunctionRWL 102 | | NodeSequenceRWL 103 | ; 104 | 105 | // utilities 106 | 107 | export const isNotEmptyContainer = (node?: NodeContainer): node is NodeContainer => { 108 | return node !== undefined && node.children.some(it => { 109 | return it.type !== 'text' || it.value.trim() !== ''; 110 | }); 111 | }; 112 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/scanner/Scanner.test.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-expression */ 2 | 3 | import { expect } from 'chai'; 4 | import 'mocha'; 5 | 6 | import { Scanner } from '../../src/scanner/Scanner'; 7 | import { Source } from '../../src/source/Source'; 8 | 9 | describe('scanner/Scanner', () => { 10 | describe('peek', () => { 11 | it('should return undefined if EOF', () => { 12 | const stream = new Scanner(new Source('ABC')); 13 | 14 | expect(stream.peek(3)).eq(undefined); 15 | expect(stream.peek(1000)).eq(undefined); 16 | }); 17 | 18 | it('should return symbol by offset', () => { 19 | const stream = new Scanner(new Source('ABC')); 20 | 21 | expect(stream.peek(1)).eq('B'); 22 | expect(stream.peek(0)).eq('A'); 23 | expect(stream.peek(2)).eq('C'); 24 | }); 25 | 26 | it('should work with next', () => { 27 | const stream = new Scanner(new Source('ABC')); 28 | 29 | stream.next(); 30 | expect(stream.peek()).eq('B'); 31 | }); 32 | 33 | it('should work with skip', () => { 34 | const stream = new Scanner(new Source('ABC')); 35 | 36 | stream.skip(2); 37 | expect(stream.peek()).eq('C'); 38 | }); 39 | }); 40 | 41 | describe('next', () => { 42 | it('should return next symbol and move pointer', () => { 43 | const stream = new Scanner(new Source('ABC')); 44 | 45 | expect(stream.next()).eq('A'); 46 | expect(stream.next()).eq('B'); 47 | expect(stream.next()).eq('C'); 48 | }); 49 | 50 | it('should work correctly with skip', () => { 51 | const stream = new Scanner(new Source('ABC')); 52 | 53 | stream.skip(1); 54 | 55 | expect(stream.next()).eq('B'); 56 | expect(stream.next()).eq('C'); 57 | }); 58 | 59 | it('should throw exception if eof', () => { 60 | const stream = new Scanner(new Source('ABC')); 61 | 62 | stream.skip(3); 63 | expect(() => stream.next()).throw(); 64 | }); 65 | 66 | it('should return empty string if eof and throwEof is set to false', () => { 67 | const stream = new Scanner(new Source('ABC')); 68 | 69 | stream.skip(3); 70 | expect(stream.next(false)).eq(''); 71 | }); 72 | }); 73 | 74 | describe('skip', () => { 75 | it('should skip given number of chars', () => { 76 | const stream = new Scanner(new Source('ABC')); 77 | 78 | expect(stream.position.offset).eq(0); 79 | 80 | stream.skip(2); 81 | 82 | expect(stream.position.offset).eq(2); 83 | }); 84 | }); 85 | 86 | describe('eof', () => { 87 | it('should return true if provider is empty', () => { 88 | const stream = new Scanner(new Source('')); 89 | 90 | expect(stream.eof()).to.be.true; 91 | }); 92 | 93 | it('should return false if provider is not empty', () => { 94 | const stream = new Scanner(new Source('Hello')); 95 | 96 | expect(stream.eof()).to.be.false; 97 | }); 98 | 99 | it('should return true when reached end', () => { 100 | const stream = new Scanner(new Source('Hello World')); 101 | 102 | stream.skip('Hello World'.length); 103 | 104 | expect(stream.eof()).to.be.true; 105 | }); 106 | 107 | it('should return false when not reached end', () => { 108 | const stream = new Scanner(new Source('Hello World')); 109 | 110 | stream.skip('Hello World'.length - 1); 111 | 112 | expect(stream.eof()).to.be.false; 113 | }); 114 | }); 115 | 116 | describe('eol', () => { 117 | it('should return true if input.peek() === \'\\n\'', () => { 118 | const source = ` 119 | Test 120 | 121 | Source 122 | laravel 123 | 124 | `.trim(); 125 | 126 | const stream = new Scanner(new Source(source)); 127 | 128 | while (!stream.eof()) { 129 | expect(stream.eol()).eq(stream.peek() === '\n'); 130 | 131 | stream.skip(); 132 | } 133 | }); 134 | }); 135 | }); 136 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/parser/Parser.test.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:object-literal-key-quotes */ 2 | 3 | import 'mocha'; 4 | import { parseAssert } from '../util.test'; 5 | 6 | describe('parser/Parser', () => { 7 | describe('parse', () => { 8 | it('should parse empty text as empty container node', () => { 9 | parseAssert('', { 10 | type: 'container', 11 | children: [], 12 | }); 13 | }); 14 | 15 | it('should parse simple text as container node with text node inside', () => { 16 | parseAssert('Hello World', { 17 | type: 'container', 18 | children: [ 19 | { 20 | type: 'text', 21 | value: 'Hello World', 22 | }, 23 | ], 24 | }); 25 | }); 26 | 27 | it('should parse simple functions', () => { 28 | parseAssert(` 29 | Hello World 30 | @testing('ABC', 15) 31 | Hello World 2 32 | `.trim(), { 33 | type: 'container', 34 | children: [ 35 | { 36 | type: 'text', 37 | value: 'Hello World\n', 38 | }, 39 | { 40 | type: 'function', 41 | name: 'testing', 42 | args: [ 43 | `'ABC'`, 44 | `15`, 45 | ], 46 | }, 47 | { 48 | type: 'text', 49 | value: '\nHello World 2', 50 | }, 51 | ], 52 | }); 53 | }); 54 | 55 | it('should parse verbatim as text', () => { 56 | parseAssert(` 57 | Text before verbatim 58 | @verbatim 59 | This is text inside verbatim 60 | There can be even functions: 61 | @yield('test') 62 | But it is still parsed as simple text 63 | @endverbatim 64 | Text after verbatim 65 | `.trim(), { 66 | type: 'container', 67 | children: [ 68 | { 69 | type: 'text', 70 | value: 'Text before verbatim\n', 71 | }, 72 | { 73 | type: 'text', 74 | value: '\nThis is text inside verbatim' + 75 | '\nThere can be even functions:' + 76 | '\n@yield(\'test\')' + 77 | '\nBut it is still parsed as simple text' + 78 | '\n', 79 | }, 80 | { 81 | type: 'text', 82 | value: '\nText after verbatim', 83 | }, 84 | ], 85 | }); 86 | }); 87 | 88 | it('should parse custom raw functions', () => { 89 | parseAssert(` 90 | Hello! 91 | @js 92 | yield 'Some text'; 93 | yield 'Some other text'; 94 | @endjs 95 | `.trim(), { 96 | type: 'container', 97 | children: [ 98 | { 99 | type: 'text', 100 | value: 'Hello!\n', 101 | }, 102 | { 103 | type: 'raw-function', 104 | name: 'js', 105 | args: undefined, 106 | content: '\nyield \'Some text\';\nyield \'Some other text\';\n', 107 | }, 108 | ], 109 | }, {}, { 110 | rawFunctions: ['js'], 111 | }); 112 | }); 113 | 114 | it('should parse sequences', () => { 115 | parseAssert(` 116 | @push('js') 117 | 118 | @endpush 119 | `.trim(), { 120 | type: 'container', 121 | children: [ 122 | { 123 | type: 'sequence', 124 | data: [ 125 | [ 126 | { 127 | type: 'function', 128 | name: 'push', 129 | args: [`'js'`], 130 | }, 131 | { 132 | type: 'container', 133 | children: [ 134 | { 135 | type: 'text', 136 | value: '\n\n', 137 | }, 138 | ], 139 | }, 140 | ], 141 | ], 142 | ending: { 143 | type: 'function', 144 | name: 'endpush', 145 | args: undefined, 146 | }, 147 | }, 148 | ], 149 | }, { 150 | sequences: { 151 | 'push': [ 152 | { name: 'endpush', required: true, multiple: false }, 153 | ], 154 | }, 155 | }); 156 | }); 157 | }); 158 | }); 159 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/ConditionalsCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from '../compiler/Compiler'; 2 | import { isNotEmptyContainer, NodeContainer } from '../parser/Node'; 3 | import { CompilerPlugin } from './CompilerPlugin'; 4 | 5 | export class ConditionalsCompilerPlugin implements CompilerPlugin { 6 | public init(compiler: Compiler): void { 7 | compiler.addSequence('if', [ 8 | { name: 'elseif', required: false, multiple: true }, 9 | { name: 'else', required: false, multiple: false }, 10 | { name: 'endif', required: true, multiple: false }, 11 | ], (delegate, node) => { 12 | for (const [prefix, suffix] of node.data) { 13 | switch (prefix.name) { 14 | case 'if': 15 | delegate.expectedArgs(prefix, 1); 16 | delegate.append('if ('); 17 | delegate.append(prefix.args![0]); 18 | delegate.append(') {\n'); 19 | break; 20 | case 'elseif': 21 | delegate.expectedArgs(prefix, 1); 22 | delegate.append('} else if ('); 23 | delegate.append(prefix.args![0]); 24 | delegate.append(') {\n'); 25 | break; 26 | case 'else': 27 | delegate.expectedArgs(prefix, 0); 28 | delegate.append('} else {\n'); 29 | break; 30 | } 31 | 32 | delegate.compileNode(suffix); 33 | } 34 | 35 | delegate.expectedArgs(node.ending, 0); 36 | delegate.append('}'); 37 | }); 38 | 39 | compiler.addSequence('unless', [ 40 | { name: 'endunless', required: true, multiple: false }, 41 | ], (delegate, node) => { 42 | const [prefix, suffix] = node.data[0]; 43 | delegate.expectedArgs(prefix, 1); 44 | delegate.expectedArgs(node.ending, 0); 45 | 46 | delegate.append('if (!('); 47 | delegate.append(prefix.args![0]); 48 | delegate.append(')) {\n'); 49 | delegate.compileContainer(suffix); 50 | delegate.append('}'); 51 | }); 52 | 53 | compiler.addSequence('switch', [ 54 | { name: 'case', required: false, multiple: true }, 55 | { name: 'break', required: false, multiple: true }, 56 | { name: 'default', required: false, multiple: false }, 57 | { name: 'break', required: false, multiple: false }, 58 | { name: 'endswitch', required: true, multiple: false }, 59 | ], (delegate, node) => { 60 | let lastSuffix: NodeContainer | undefined; 61 | 62 | for (const [prefix, suffix] of node.data) { 63 | switch (prefix.name) { 64 | case 'switch': 65 | delegate.expectedArgs(prefix, 1); 66 | delegate.append('switch ('); 67 | delegate.append(prefix.args![0]); 68 | delegate.append(') {\n'); 69 | 70 | if (isNotEmptyContainer(suffix)) { 71 | return delegate.error('unexpected content between @case/@default and @break', suffix); 72 | } 73 | break; 74 | case 'case': 75 | delegate.expectedArgs(prefix, 1); 76 | delegate.append('case ('); 77 | delegate.append(prefix.args![0]); 78 | delegate.append('):\n'); 79 | 80 | if (isNotEmptyContainer(lastSuffix)) { 81 | return delegate.error('unexpected content between @case/@default and @break', lastSuffix); 82 | } 83 | break; 84 | case 'break': 85 | delegate.expectedArgs(prefix, 0); 86 | 87 | if (lastSuffix === undefined) { 88 | return delegate.error('unexpected break', prefix); 89 | } 90 | delegate.append('{\n'); 91 | delegate.compileNode(lastSuffix); 92 | delegate.append('} break;\n'); 93 | 94 | if (isNotEmptyContainer(suffix)) { 95 | return delegate.error('unexpected content between @case/@default and @break', suffix); 96 | } 97 | break; 98 | case 'default': 99 | delegate.expectedArgs(prefix, 0); 100 | delegate.append('default:\n'); 101 | break; 102 | } 103 | lastSuffix = suffix; 104 | } 105 | 106 | if (lastSuffix !== undefined) { 107 | delegate.append('{\n'); 108 | delegate.compileNode(lastSuffix); 109 | delegate.append('} break;\n'); 110 | } 111 | 112 | delegate.expectedArgs(node.ending, 0); 113 | delegate.append('}'); 114 | return undefined; 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/plugin/LoopsCompilerPlugin.ts: -------------------------------------------------------------------------------- 1 | import { Compiler, SequenceCompiler } from '../compiler/Compiler'; 2 | import { CompilerPlugin } from './CompilerPlugin'; 3 | 4 | export class LoopsCompilerPlugin implements CompilerPlugin { 5 | public init(compiler: Compiler): void { 6 | compiler.addFunction('break', (delegate, node) => { 7 | delegate.expectedArgs(node, 0, 1); 8 | 9 | if (node.args && node.args.length === 1) { 10 | delegate.append('if ('); 11 | delegate.append(node.args[0]); 12 | delegate.append(') break;'); 13 | } else { 14 | delegate.append('break;'); 15 | } 16 | }); 17 | 18 | compiler.addFunction('continue', (delegate, node) => { 19 | delegate.expectedArgs(node, 0, 1); 20 | 21 | if (node.args && node.args.length === 1) { 22 | delegate.append('if ('); 23 | delegate.append(node.args[0]); 24 | delegate.append(') continue;'); 25 | } else { 26 | delegate.append('continue;'); 27 | } 28 | }); 29 | 30 | compiler.addSequence('for', [ 31 | { name: 'endfor', required: true, multiple: false }, 32 | ], (delegate, node) => { 33 | const [prefix, suffix] = node.data[0]; 34 | delegate.expectedArgs(prefix, 1, Infinity); 35 | delegate.expectedArgs(node.ending, 0); 36 | 37 | delegate.append('for ('); 38 | delegate.append(prefix.args!.join(', ')); 39 | delegate.append(') {\n'); 40 | delegate.compileContainer(suffix); 41 | delegate.append('}'); 42 | }); 43 | 44 | const foreachCompiler: SequenceCompiler = (delegate, node) => { 45 | const [prefix, suffix] = node.data[0]; 46 | const hasEmpty = node.data.length === 2; 47 | delegate.expectedArgs(prefix, 1, Infinity); 48 | delegate.expectedArgs(node.ending, 0); 49 | if (hasEmpty) { 50 | delegate.expectedArgs(node.data[1][0], 0); 51 | } 52 | 53 | const args = prefix.args!.join(', '); 54 | const matches = args.match(/^\s*(.+)\s+as\s+(?:([$\w]+)\s*=>\s*)?([$\w]+)\s*$/); 55 | if (matches === null) { 56 | return delegate.error( 57 | `foreach body should have format 'iteratee as value' or 'iteratee as key => value'`, 58 | node, 59 | { 60 | start: prefix.start.relative(1 + prefix.name.length), 61 | end: prefix.end, 62 | }, 63 | ); 64 | } 65 | 66 | const [, v_iteratee, v_key, v_value] = matches; 67 | 68 | if (hasEmpty) { 69 | // TODO: Fix redeclare 70 | delegate.append('let __empty = true;\n'); 71 | } 72 | delegate.append('for (const loop = __env.pushLoop('); 73 | delegate.append(v_iteratee); 74 | delegate.append('); loop.__hasRemaining; loop.__next()) {\n'); 75 | if (hasEmpty) { 76 | delegate.append('__empty = false;\n'); 77 | } 78 | if (v_key !== null) { 79 | delegate.append('const '); 80 | delegate.append(v_key); 81 | delegate.append(' = loop.__key;\n'); 82 | } 83 | delegate.append('const '); 84 | delegate.append(v_value); 85 | delegate.append(' = loop.__value;\n'); 86 | delegate.compileContainer(suffix); 87 | delegate.append('}\n'); 88 | delegate.append('__env.popLoop();'); 89 | if (hasEmpty) { 90 | delegate.append('\nif (__empty) {\n'); 91 | delegate.compileContainer(node.data[1][1]); 92 | delegate.append('}'); 93 | } 94 | return undefined; 95 | }; 96 | 97 | compiler.addSequence('foreach', [ 98 | { name: 'endforeach', required: true, multiple: false }, 99 | ], foreachCompiler); 100 | 101 | compiler.addSequence('forelse', [ 102 | { name: 'empty', required: true, multiple: false }, 103 | { name: 'endforelse', required: true, multiple: false }, 104 | ], foreachCompiler); 105 | 106 | compiler.addSequence('while', [ 107 | { name: 'endwhile', required: true, multiple: false }, 108 | ], (delegate, node) => { 109 | const [prefix, suffix] = node.data[0]; 110 | delegate.expectedArgs(prefix, 1, Infinity); 111 | delegate.expectedArgs(node.ending, 0); 112 | 113 | delegate.append('while ('); 114 | delegate.append(prefix.args!.join(', ')); 115 | delegate.append(') {\n'); 116 | delegate.compileContainer(suffix); 117 | delegate.append('}'); 118 | }); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/types/Position.test.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-expression */ 2 | 3 | import { expect } from 'chai'; 4 | import 'mocha'; 5 | 6 | import { Position } from '../../src/source/Position'; 7 | import { Source } from '../../src/source/Source'; 8 | 9 | describe('types/Position', () => { 10 | describe('line', () => { 11 | it('should be starting from 1', () => { 12 | const pos = new Position(new Source(''), 0); 13 | 14 | expect(pos.line).eq(1); 15 | }); 16 | 17 | it('should treat \\n as symbol on previous line', () => { 18 | const pos1 = new Position(new Source('Hello\nWorld\n'), 5); 19 | const pos2 = new Position(new Source('Hello\nWorld\n'), 11); 20 | 21 | expect(pos1.line).eq(1); 22 | expect(pos2.line).eq(2); 23 | }); 24 | 25 | it('should return line of the position', () => { 26 | const pos = new Position(new Source('Hello\nWorld\n'), 9); 27 | 28 | expect(pos.line).eq(2); 29 | }); 30 | }); 31 | 32 | describe('column', () => { 33 | it('should be starting from 1', () => { 34 | const pos = new Position(new Source(''), 0); 35 | 36 | expect(pos.column).eq(1); 37 | }); 38 | 39 | it('should treat \\n as symbol on previous line', () => { 40 | const pos1 = new Position(new Source('Hello\nWorld\n'), 5); 41 | const pos2 = new Position(new Source('Hello\nWorld\n'), 11); 42 | 43 | expect(pos1.column).eq(6); 44 | expect(pos2.column).eq(6); 45 | }); 46 | 47 | it('should return column of the position', () => { 48 | const pos = new Position(new Source('Hello\nWorld\n'), 9); 49 | 50 | expect(pos.column).eq(4); 51 | }); 52 | }); 53 | 54 | describe('relative', () => { 55 | it('should accept negative values', () => { 56 | const pos1 = new Position(new Source('Hello\nWorld'), 8); 57 | const pos2 = pos1.relative(-4); 58 | 59 | expect(pos2.offset).eq(4); 60 | expect(pos2.line).eq(1); 61 | expect(pos2.column).eq(5); 62 | }); 63 | 64 | it('should accept positive values', () => { 65 | const pos1 = new Position(new Source('Hello\nWorld'), 3); 66 | const pos2 = pos1.relative(5); 67 | 68 | expect(pos2.offset).eq(8); 69 | expect(pos2.line).eq(2); 70 | expect(pos2.column).eq(3); 71 | }); 72 | 73 | it('should return to the same position if using values with sum 0', () => { 74 | const pos1 = new Position(new Source('Hello\nWorld'), 8); 75 | const pos2 = pos1.relative(-4); 76 | const pos3 = pos2.relative(4); 77 | 78 | expect(pos1.equals(pos3)).true; 79 | }); 80 | }); 81 | 82 | describe('until', () => { 83 | it('should return text between two positions', () => { 84 | const source = new Source('Hello\nWorld\n'); 85 | const pos1 = new Position(source, 4); 86 | const pos2 = new Position(source, 8); 87 | 88 | expect(pos1.until(pos2)).eq('o\nWo'); 89 | }); 90 | 91 | it('should work with inverted order of arguments', () => { 92 | const source = new Source('Hello\nWorld\n'); 93 | const pos1 = new Position(source, 4); 94 | const pos2 = new Position(source, 8); 95 | 96 | expect(pos2.until(pos1)).eq('o\nWo'); 97 | }); 98 | }); 99 | 100 | describe('equals', () => { 101 | it('should return false when different sources', () => { 102 | const pos1 = new Position(new Source('Hello World'), 4); 103 | const pos2 = new Position(new Source('Hello World'), 4); 104 | 105 | expect(pos1.equals(pos2)).false; 106 | expect(pos2.equals(pos1)).false; 107 | }); 108 | 109 | it('should return false when different offsets', () => { 110 | const source = new Source('Hello World'); 111 | const pos1 = new Position(source, 4); 112 | const pos2 = new Position(source, 8); 113 | 114 | expect(pos1.equals(pos2)).false; 115 | expect(pos2.equals(pos1)).false; 116 | }); 117 | 118 | it('should return true when same sources and offsets', () => { 119 | const source = new Source('Hello World'); 120 | const pos1 = new Position(source, 7); 121 | const pos2 = new Position(source, 7); 122 | 123 | expect(pos1.equals(pos2)).true; 124 | expect(pos2.equals(pos1)).true; 125 | }); 126 | }); 127 | 128 | describe('toString', () => { 129 | it('should return string representation (line:column) of position', () => { 130 | const source = new Source('Hello World'); 131 | const pos = new Position(source, 3); 132 | 133 | expect(pos.toString()).eq('1:4'); 134 | }); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/parser/Parser.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable:object-literal-key-quotes 2 | 3 | import { ParserError } from '../error/ParserError'; 4 | import { Lexer } from '../lexer/Lexer'; 5 | import { FullToken } from '../lexer/Token'; 6 | import { Location } from '../source/Location'; 7 | import { Node, NodeContainer, NodeFunction, NodeWithoutLocation } from './Node'; 8 | 9 | export type ParserSequenceItemConfig = { 10 | name: string | ReadonlyArray, 11 | required: boolean, 12 | multiple: boolean, 13 | }; 14 | 15 | export type ParserSequenceConfig = 16 | | ReadonlyArray 17 | | ((args: ReadonlyArray) => ReadonlyArray) 18 | ; 19 | 20 | export interface ParserConfig { 21 | sequences: { [fn_open: string]: ParserSequenceConfig }; 22 | } 23 | 24 | export const DEFAULT_PARSER_CONFIG: ParserConfig = { 25 | sequences: {}, 26 | }; 27 | 28 | export const parse = (lexer: Lexer, parserConfig?: Partial): Node => { 29 | // @ts-ignore 30 | const config: ParserConfig = { 31 | ...DEFAULT_PARSER_CONFIG, 32 | ...parserConfig, 33 | }; 34 | 35 | const { sequences } = config; 36 | 37 | const breakingFunctions: string[][] = []; 38 | 39 | const error = (message: string, position?: Location): never => { 40 | if (position === undefined) { 41 | position = { 42 | start: lexer.position, 43 | end: lexer.position, 44 | }; 45 | } 46 | 47 | throw new ParserError(lexer.source, { 48 | ...position, 49 | }, message); 50 | }; 51 | 52 | const unexpectedToken = (token: FullToken = lexer.peek(), suffix?: string): never => { 53 | return error(`unexpected token '${token.type}'` + (suffix ? `, ${suffix}` : ''), { 54 | start: token.start, 55 | end: token.end, 56 | }); 57 | }; 58 | 59 | const parser = ( 60 | fn: (...args: T[]) => N, 61 | ): ((...args: T[]) => (N & Location) | undefined) => { 62 | return (...args: T[]): (N & Location) | undefined => { 63 | const start = lexer.peek().start; 64 | const tok = fn(...args); 65 | if (tok === undefined) { 66 | return undefined; 67 | } 68 | 69 | const end = lexer.peek().start; 70 | 71 | return { 72 | start, 73 | end, 74 | ...tok, 75 | }; 76 | }; 77 | }; 78 | 79 | const parseRoot = parser(() => { 80 | const nodes: Node[] = []; 81 | 82 | let node: Node | undefined; 83 | while (undefined !== (node = parseAny())) { 84 | nodes.push(node); 85 | } 86 | 87 | return { 88 | type: 'container', 89 | children: nodes, 90 | }; 91 | }); 92 | 93 | const parseAny = parser((): NodeWithoutLocation | undefined => { 94 | const tok = lexer.peek(); 95 | if ( 96 | tok.type === 'function' && 97 | breakingFunctions.length !== 0 && 98 | breakingFunctions[breakingFunctions.length - 1].indexOf(tok.name) !== -1 99 | ) { 100 | breakingFunctions.pop(); 101 | return undefined; 102 | } 103 | 104 | lexer.next(); 105 | 106 | switch (tok.type) { 107 | case 'eof': 108 | return undefined; 109 | case 'comment': 110 | return { 111 | type: 'comment', 112 | value: tok.value, 113 | }; 114 | case 'text': 115 | return { 116 | type: 'text', 117 | value: tok.value, 118 | }; 119 | case 'data': 120 | return { 121 | type: 'data', 122 | escaped: tok.escaped, 123 | value: tok.value, 124 | filters: tok.filters, 125 | }; 126 | case 'function': 127 | const fn: Node = { 128 | start: tok.start, 129 | end: tok.end, 130 | type: 'function', 131 | name: tok.name, 132 | args: tok.args, 133 | }; 134 | 135 | if (tok.name in sequences) { 136 | const sequence = 137 | typeof sequences[tok.name] === 'function' 138 | // @ts-ignore 139 | ? sequences[tok.name](tok.args) 140 | : sequences[tok.name] 141 | ; 142 | let i = 0; 143 | 144 | let prefixFunction: NodeFunction = fn; 145 | const data: Array<[NodeFunction, NodeContainer]> = []; 146 | 147 | while (i < sequence.length) { 148 | const bf = []; 149 | for (let j = i; j < sequence.length; ++j) { 150 | const element = sequence[j]; 151 | bf.push(...( 152 | typeof element.name === 'string' 153 | ? [element.name] 154 | : element.name 155 | )); 156 | 157 | if (element.required) { 158 | break; 159 | } 160 | } 161 | breakingFunctions.push(bf); 162 | 163 | const content = parseRoot()!; 164 | data.push([prefixFunction, content]); 165 | 166 | const tok2 = lexer.next(); 167 | 168 | // TODO: handler required === true && multiple === true 169 | let found = false; 170 | if (tok2.type === 'function') { 171 | for (let j = i; j < sequence.length; ++j) { 172 | const element = sequence[j]; 173 | if ( 174 | typeof element.name === 'string' 175 | ? element.name === tok2.name 176 | : element.name.indexOf(tok2.name) !== -1 177 | ) { 178 | if (!element.multiple) { 179 | i = ++j; 180 | } 181 | found = true; 182 | break; 183 | } 184 | } 185 | } 186 | if (!found || tok2.type !== 'function') { 187 | const expected = breakingFunctions[breakingFunctions.length - 1] 188 | .map(it => '@' + it) 189 | .join(', ') 190 | ; 191 | return unexpectedToken(tok2, `expected one of: ${expected}`); 192 | } 193 | 194 | prefixFunction = { 195 | start: tok2.start, 196 | end: tok2.end, 197 | type: 'function', 198 | name: tok2.name, 199 | args: tok2.args, 200 | }; 201 | } 202 | 203 | return { 204 | type: 'sequence', 205 | data, 206 | ending: prefixFunction, 207 | }; 208 | } 209 | 210 | return fn; 211 | case 'raw-function': 212 | return { 213 | type: 'raw-function', 214 | name: tok.name, 215 | args: tok.args, 216 | content: tok.content, 217 | }; 218 | } 219 | 220 | return unexpectedToken(tok); 221 | }); 222 | 223 | const root = parseRoot()!; 224 | 225 | if (!lexer.eof()) { 226 | return unexpectedToken(); 227 | } 228 | 229 | return root; 230 | }; 231 | -------------------------------------------------------------------------------- /packages/bladejs/src/runtime/Runtime.ts: -------------------------------------------------------------------------------- 1 | import { DefaultEnvironment } from '../environment/DefaultEnvironment'; 2 | import { Environment, TemplateParams } from '../environment/Environment'; 3 | import { CompiledTemplate } from '../template/CompiledTemplate'; 4 | import { TemplateProvider } from '../template/TemplateProvider'; 5 | import '../util/asyncIterator'; 6 | import { trimAsyncIterable } from '../util/trimAsyncIterable'; 7 | import { RuntimeFilter } from './RuntimeFilter'; 8 | import { RuntimeFunction } from './RuntimeFunction'; 9 | 10 | type CompiledTemplateItem = { 11 | readonly promise: Promise; 12 | 13 | provider?: TemplateProvider; 14 | value?: CompiledTemplate; 15 | readonly time: number; 16 | }; 17 | 18 | export class Runtime { 19 | private readonly providers: ReadonlyArray; 20 | private readonly compiledTemplates = new Map(); 21 | private readonly filters = new Map(); 22 | private readonly functions = new Map(); 23 | 24 | private _cacheEnabled: boolean = true; 25 | 26 | public get cacheEnabled(): boolean { 27 | return this._cacheEnabled; 28 | } 29 | 30 | public set cacheEnabled(enabled: boolean) { 31 | this._cacheEnabled = enabled; 32 | if (!enabled) { 33 | this.compiledTemplates.clear(); 34 | } 35 | } 36 | 37 | public constructor(providers: ReadonlyArray) { 38 | this.providers = providers; 39 | 40 | this.registerFilter('currency', (_env, value, currency = '$', left = true) => 41 | left 42 | ? `${currency} ${value}` 43 | : `${value} ${currency}` 44 | , 45 | ); 46 | 47 | this.registerFilter('trim', (_env, value) => `${value}`.trim()); 48 | 49 | this.registerFilter('substr', (_env, value, start, length) => 50 | `${value}`.substring(start, length === undefined ? undefined : start + length) 51 | , 52 | ); 53 | 54 | this.registerFilter('substring', (_env, value, start, end) => 55 | `${value}`.substring(start, end) 56 | , 57 | ); 58 | 59 | this.registerFilter('ucfirst', (_env, value) => { 60 | const str = `${value}`; 61 | return str.substring(0, 1).toUpperCase() + str.substring(1); 62 | }); 63 | 64 | this.registerFilter('lcfirst', (_env, value) => { 65 | const str = `${value}`; 66 | return str.substring(0, 1).toLowerCase() + str.substring(1); 67 | }); 68 | 69 | this.registerFilter('reverse', (_env, value) => 70 | `${value}`.split('').reverse().join('') 71 | , 72 | ); 73 | 74 | this.registerFunction('include', async function* (env, includedName, params) { 75 | yield* env.runtime.render(includedName, params); 76 | }); 77 | 78 | this.registerFunction('includeIf', async function* (env, includedName, params) { 79 | try { 80 | yield* env.runtime.render(includedName, params); 81 | } catch (e) { 82 | if (!(e instanceof Error && e.message.startsWith('Could not find template '))) { 83 | throw e; 84 | } 85 | } 86 | }); 87 | 88 | this.registerFunction('includeWhen', async function* (env, condition, includedName, params) { 89 | if (condition) { 90 | yield* env.runtime.render(includedName, params); 91 | } 92 | }); 93 | 94 | this.registerFunction('includeFirst', async function* (env, includedNames, params) { 95 | for (const includedName of includedNames) { 96 | try { 97 | yield* env.runtime.render(includedName, params); 98 | return; 99 | } catch (e) { 100 | if (!(e instanceof Error && e.message.startsWith('Could not find template '))) { 101 | throw e; 102 | } 103 | } 104 | } 105 | throw new Error(`Could not find template ${includedNames.map((it: any) => `'${it}'`).join(' or ')}`); 106 | }); 107 | 108 | this.registerFunction('each', async function* (env, includedName, collection, varName, emptyIncludedName) { 109 | let empty = true; 110 | for (const loop = env.pushLoop(collection); loop.__hasRemaining; loop.__next()) { 111 | empty = false; 112 | 113 | yield* env.runtime.render(includedName, { 114 | [varName]: loop.__value, 115 | }); 116 | } 117 | env.popLoop(); 118 | 119 | if (empty && emptyIncludedName !== undefined) { 120 | yield* env.runtime.render(emptyIncludedName); 121 | } 122 | }); 123 | } 124 | 125 | public async *render(name: string, params: TemplateParams = {}): AsyncIterable { 126 | const env = new DefaultEnvironment(this, params); 127 | yield* trimAsyncIterable(this.renderInternal(name, env)); 128 | } 129 | 130 | public async *renderInternal(name: string, environment: Environment): AsyncIterable { 131 | const template = await this.getTemplate(name); 132 | yield* environment.process(template.call(environment, environment)); 133 | } 134 | 135 | public getFilter(name: string): RuntimeFilter { 136 | const fn = this.filters.get(name); 137 | if (fn === undefined) { 138 | throw new Error(`Filter '${name}' does not exist.`); 139 | } 140 | return fn; 141 | } 142 | 143 | public registerFilter(name: string, fn: RuntimeFilter, force: boolean = false): void { 144 | if (!force && this.filters.has(name)) { 145 | throw new Error(`Filter '${name}' is already registered.`); 146 | } 147 | this.filters.set(name, fn); 148 | } 149 | 150 | public getFunction(name: string): RuntimeFunction { 151 | const fn = this.functions.get(name); 152 | if (fn === undefined) { 153 | throw new Error(`Function '${name}' does not exist.`); 154 | } 155 | return fn; 156 | } 157 | 158 | public registerFunction(name: string, fn: RuntimeFunction, force: boolean = false): void { 159 | if (!force && this.functions.has(name)) { 160 | throw new Error(`Function '${name}' is already registered.`); 161 | } 162 | this.functions.set(name, fn); 163 | } 164 | 165 | public aliasInclude(view: string, name: string): void { 166 | this.registerFunction(name, async function* (env, params): AsyncIterable { 167 | yield* env.runtime.render(view, params); 168 | }); 169 | } 170 | 171 | public aliasComponent(view: string, name: string): void { 172 | this.registerFunction(name, async function* (env, params): AsyncIterable { 173 | env.beginComponent(view, params); 174 | }); 175 | 176 | this.registerFunction('end' + name, async function* (env): AsyncIterable { 177 | yield* env.endComponent(); 178 | }); 179 | } 180 | 181 | private async getTemplate(name: string): Promise { 182 | if (this._cacheEnabled && this.compiledTemplates.has(name)) { 183 | const item = this.compiledTemplates.get(name)!; 184 | 185 | if (item.value !== undefined && await item.provider!.isOutdated(name, item.value, item.time)) { 186 | this.compiledTemplates.delete(name); 187 | } else { 188 | return item.promise; 189 | } 190 | } 191 | 192 | const promise = (async (): Promise<[TemplateProvider, CompiledTemplate]> => { 193 | for (const source of this.providers) { 194 | const compiled = await source.getTemplateCompiledFile(name); 195 | if (compiled !== undefined) { 196 | return [source, compiled]; 197 | } 198 | } 199 | 200 | throw new Error(`Could not find template ${name}`); 201 | })(); 202 | 203 | if (this._cacheEnabled) { 204 | const item: CompiledTemplateItem = { 205 | promise: promise.then(([, value]) => value), 206 | 207 | value: undefined, 208 | time: Date.now(), 209 | }; 210 | 211 | this.compiledTemplates.set(name, item); 212 | 213 | promise.then(([source, value]) => { 214 | item.provider = source; 215 | item.value = value; 216 | }); 217 | 218 | // Retry later on error 219 | promise.catch(() => this.compiledTemplates.delete(name)); 220 | } 221 | 222 | return (await promise)[1]; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/test/lexer/Lexer.test.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-expression */ 2 | 3 | import { expect } from 'chai'; 4 | import 'mocha'; 5 | import { createLexerFromText, lexAssert } from '../util.test'; 6 | 7 | import { Token } from '../../src/lexer/Token'; 8 | 9 | describe('lexer/Lexer', () => { 10 | describe('peek', () => { 11 | it('should return eof token when provider is empty', () => { 12 | const lexer = createLexerFromText(''); 13 | 14 | expect(lexer.peek().type).eq('eof'); 15 | }); 16 | 17 | it('should return next token without moving', () => { 18 | const lexer = createLexerFromText('Hello World'); 19 | 20 | expect(lexer.peek() === lexer.peek()).true; 21 | }); 22 | }); 23 | 24 | describe('next', () => { 25 | it('should return eof token when provider is empty', () => { 26 | const lexer = createLexerFromText(''); 27 | 28 | for (let i = 0; i < 100; ++i) { 29 | expect(lexer.peek().type).eq('eof'); 30 | } 31 | }); 32 | 33 | it('should move thru tokens', () => { 34 | const lexer = createLexerFromText(` 35 | Hello World 36 | @section('a', 'b') 37 | `.trim()); 38 | 39 | let tok: Token; 40 | 41 | tok = lexer.next(); 42 | expect(tok.type).eq('text'); 43 | if (tok.type === 'text') { 44 | expect(tok.value).eq('Hello World\n'); 45 | } 46 | 47 | tok = lexer.next(); 48 | expect(tok.type).eq('function'); 49 | if (tok.type === 'function') { 50 | expect(tok.name).eq('section'); 51 | expect(tok.args).deep.eq([`'a'`, `'b'`]); 52 | } 53 | }); 54 | 55 | it('should return first token even if peek was called before', () => { 56 | const lexer = createLexerFromText(` 57 | @for(let i = 0; i < 10; ++i) 58 | @endfor 59 | `.trim()); 60 | 61 | expect(lexer.peek()).deep.eq(lexer.next()); 62 | }); 63 | }); 64 | 65 | describe('samples', () => { 66 | it('sample-1 (complex input)', () => { 67 | lexAssert(` 68 | {{-- this example is taken from https://laravel.com/docs/5.8/blade#template-inheritance --}} 69 | 70 | 71 | 72 | 73 | 74 | App Name - @yield('title') 75 | 76 | 77 | @section('sidebar') 78 | This is the master sidebar. 79 | @show 80 | 81 |
82 | @yield('content') 83 |
84 | 85 | 86 | `.trim(), [ 87 | { 88 | type: 'comment', 89 | value: ' this example is taken from https://laravel.com/docs/5.8/blade#template-inheritance ', 90 | }, 91 | { 92 | type: 'text', 93 | value: 94 | '\n\n' + 95 | '\n\n\n \n App Name - ' 96 | , 97 | }, 98 | { 99 | type: 'function', 100 | name: 'yield', 101 | args: [ 102 | `'title'`, 103 | ], 104 | }, 105 | { 106 | type: 'text', 107 | value: '\n \n \n ', 108 | }, 109 | { 110 | type: 'function', 111 | name: 'section', 112 | args: [ 113 | `'sidebar'`, 114 | ], 115 | }, 116 | { 117 | type: 'text', 118 | value: '\n This is the master sidebar.\n ', 119 | }, 120 | { 121 | type: 'function', 122 | name: 'show', 123 | }, 124 | { 125 | type: 'text', 126 | value: '\n\n
\n ', 127 | }, 128 | { 129 | type: 'function', 130 | name: 'yield', 131 | args: [ 132 | `'content'`, 133 | ], 134 | }, 135 | { 136 | type: 'text', 137 | value: '\n
\n \n', 138 | }, 139 | ]); 140 | }); 141 | 142 | it('sample-2 (function arguments with nested braces and quotes)', () => { 143 | lexAssert(` 144 | @include('view.name', { 145 | 'foo': ['hello )))', '(( world'], 146 | 'bar': 5 * (3 + 4 * (5 - 2)), 147 | }) 148 | `.trim(), [ 149 | { 150 | type: 'function', 151 | name: 'include', 152 | args: [ 153 | `'view.name'`, 154 | `{ 155 | 'foo': ['hello )))', '(( world'], 156 | 'bar': 5 * (3 + 4 * (5 - 2)), 157 | }`, 158 | ], 159 | }, 160 | ]); 161 | }); 162 | 163 | it('sample-3 (\'{{ }}\' and \'{!! !!}\')', () => { 164 | lexAssert(` 165 |
166 | Username: {{ user.name }} 167 | Status: {{ user.status }} 168 | Badge: {!! user.badge !!} 169 |
170 | `.trim(), [ 171 | { 172 | type: 'text', 173 | value: '
\n Username: ', 174 | }, 175 | { 176 | type: 'data', 177 | escaped: true, 178 | value: 'user.name', 179 | }, 180 | { 181 | type: 'text', 182 | value: '\n Status: ', 183 | }, 184 | { 185 | type: 'data', 186 | escaped: true, 187 | value: 'user.status', 188 | }, 189 | { 190 | type: 'text', 191 | value: '\n Badge: ', 192 | }, 193 | { 194 | type: 'data', 195 | escaped: false, 196 | value: 'user.badge', 197 | }, 198 | { 199 | type: 'text', 200 | value: '\n
', 201 | }, 202 | ]); 203 | }); 204 | 205 | it('sample-4 (escape with \'@\')', () => { 206 | lexAssert(` 207 |
208 | Username: @{{ user.name }} 209 | Status: @{{ user.status }} 210 | Badge: @{!! user.badge !!} 211 |
212 | `.trim(), [ 213 | { 214 | type: 'text', 215 | value: 216 | '
\n Username: {{ user.name }}\n Status: {{ user.status }}\n Badge: {!! user.badge !!}\n
', 217 | }, 218 | ]); 219 | }); 220 | 221 | it('sample-5 (verbatim and js)', () => { 222 | lexAssert(` 223 | @verbatim 224 | Hello World 225 | @endverbatim 226 | 227 | @js 228 | for (let i = 0; i < 10; ++i) { 229 | print(i); 230 | } 231 | @endjs 232 | 233 | @markdown('default') 234 | 235 | Profile info: 236 | - Name: Bill 237 | - Number: *+1234567890* 238 | - Email: example@example.com 239 | 240 | @endmarkdown 241 | `.trim(), [ 242 | { 243 | type: 'text', 244 | value: '\nHello World\n', 245 | }, 246 | { 247 | type: 'text', 248 | value: '\n\n', 249 | }, 250 | { 251 | type: 'raw-function', 252 | name: 'js', 253 | content: '\nfor (let i = 0; i < 10; ++i) {\n print(i);\n}\n', 254 | }, 255 | { 256 | type: 'text', 257 | value: '\n\n', 258 | }, 259 | { 260 | type: 'raw-function', 261 | name: 'markdown', 262 | args: [`'default'`], 263 | content: '\n\nProfile info:\n- Name: Bill\n- Number: *+1234567890*\n- Email: example@example.com\n\n', 264 | }, 265 | ], { 266 | rawFunctions: ['verbatim', 'js', 'markdown'], 267 | }); 268 | }); 269 | 270 | it('sample-6 (filters)', () => { 271 | lexAssert(` 272 | {{ 'test' | ucfirst }} 273 | {{ 'hello' | ucfirst | uclast }} 274 | {{ ' HeLlO ' | trim }} 275 | {{ name | ucfirst | substr:0,1 }} 276 | {!! time | format-time:'Y-m-d H:i:s' !!} 277 | `.trim(), [ 278 | { 279 | type: 'data', 280 | escaped: true, 281 | value: '\'test\'', 282 | filters: [ 283 | { name: 'ucfirst', args: [] }, 284 | ], 285 | }, 286 | { 287 | type: 'text', 288 | value: '\n', 289 | }, 290 | { 291 | type: 'data', 292 | escaped: true, 293 | value: '\'hello\'', 294 | filters: [ 295 | { name: 'ucfirst', args: [] }, 296 | { name: 'uclast', args: [] }, 297 | ], 298 | }, 299 | { 300 | type: 'text', 301 | value: '\n', 302 | }, 303 | { 304 | type: 'data', 305 | escaped: true, 306 | value: '\' HeLlO \'', 307 | filters: [ 308 | { name: 'trim', args: [] }, 309 | ], 310 | }, 311 | { 312 | type: 'text', 313 | value: '\n', 314 | }, 315 | { 316 | type: 'data', 317 | escaped: true, 318 | value: 'name', 319 | filters: [ 320 | { name: 'ucfirst', args: [] }, 321 | { name: 'substr', args: ['0', '1'] }, 322 | ], 323 | }, 324 | { 325 | type: 'text', 326 | value: '\n', 327 | }, 328 | { 329 | type: 'data', 330 | escaped: false, 331 | value: 'time', 332 | filters: [ 333 | { name: 'format-time', args: ['\'Y-m-d H:i:s\''] }, 334 | ], 335 | }, 336 | ]); 337 | }); 338 | }); 339 | }); 340 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/compiler/Compiler.ts: -------------------------------------------------------------------------------- 1 | import transpile = require('vue-template-es2015-compiler'); 2 | import { CompilerError } from '../error/CompilerError'; 3 | import { createLexer } from '../lexer/Lexer'; 4 | import { 5 | BaseFunction, 6 | Node, 7 | NodeComment, 8 | NodeContainer, 9 | NodeData, 10 | NodeFunction, 11 | NodeRawFunction, 12 | NodeSequence, 13 | NodeText, 14 | } from '../parser/Node'; 15 | import { parse, ParserSequenceConfig } from '../parser/Parser'; 16 | import { CompilerPlugin } from '../plugin/CompilerPlugin'; 17 | import { ComponentsCompilerPlugin } from '../plugin/ComponentsCompilerPlugin'; 18 | import { ConditionalsCompilerPlugin } from '../plugin/ConditionalsCompilerPlugin'; 19 | import { IncludesCompilerPlugin } from '../plugin/IncludesCompilerPlugin'; 20 | import { JsBlockCompilerPlugin } from '../plugin/JsBlockCompilerPlugin'; 21 | import { JsonCompilerPlugin } from '../plugin/JsonCompilerPlugin'; 22 | import { LayoutCompilerPlugin } from '../plugin/LayoutCompilerPlugin'; 23 | import { LoopsCompilerPlugin } from '../plugin/LoopsCompilerPlugin'; 24 | import { StacksCompilerPlugin } from '../plugin/StacksCompilerPlugin'; 25 | import { Scanner } from '../scanner/Scanner'; 26 | import { Location } from '../source/Location'; 27 | import { Source } from '../source/Source'; 28 | import { createStringBuilder, StringBuilder } from '../string/StringBuilder'; 29 | import { CompilerDelegate } from './CompilerDelegate'; 30 | 31 | export type NodeCompiler = (delegate: CompilerDelegate, node: T) => void; 32 | export type FunctionCompiler = NodeCompiler; 33 | export type SequenceCompiler = NodeCompiler; 34 | export type RawFunctionCompiler = NodeCompiler; 35 | 36 | export class Compiler { 37 | private builder!: StringBuilder; 38 | private footerBuilder!: StringBuilder; 39 | 40 | private functions: { [name: string]: FunctionCompiler } = {}; 41 | private sequences: { [name: string]: SequenceCompiler } = {}; 42 | private rawFunctions: { [name: string]: RawFunctionCompiler } = {}; 43 | 44 | private parserSequences: { [name: string]: ParserSequenceConfig } = {}; 45 | 46 | private delegate: CompilerDelegate = { 47 | append: arg => this.builder.append(arg), 48 | footer: { 49 | append: arg => this.footerBuilder.append(arg), 50 | }, 51 | 52 | compileContainer: this.compileContainer.bind(this), 53 | compileComment: this.compileComment.bind(this), 54 | compileText: this.compileText.bind(this), 55 | compileData: this.compileData.bind(this), 56 | compileFunction: this.compileFunction.bind(this), 57 | compileRawFunction: this.compileRawFunction.bind(this), 58 | compileSequence: this.compileSequence.bind(this), 59 | compileNode: this.compileNode.bind(this), 60 | 61 | error: this.error.bind(this), 62 | 63 | expectedArgs: (node: Node & BaseFunction, min: number, max: number = min): never | void => { 64 | const argsCount = node.args !== undefined ? node.args.length : 0; 65 | if (argsCount < min || argsCount > max) { 66 | const expected = min === max 67 | ? (min === 1 ? `1 argument` : `${min} arguments`) 68 | : `${min}-${max} arguments` 69 | ; 70 | throw this.error(`expected ${expected}, got ${argsCount}`, node, { 71 | start: node.start.relative(1 + node.name.length), 72 | end: node.end, 73 | }); 74 | } 75 | }, 76 | }; 77 | 78 | public addPlugin(cons: new () => CompilerPlugin): void { 79 | const plugin = new cons(); 80 | plugin.init(this); 81 | } 82 | 83 | public addFunction(name: string, compiler: FunctionCompiler): void { 84 | this.functions[name] = compiler; 85 | } 86 | 87 | public addSimpleFunction(name: string, min: number, max: number = min): void { 88 | this.addFunction(name, (builder, node) => { 89 | builder.expectedArgs(node, min, max); 90 | 91 | const args = node.args || []; 92 | builder.append('yield* __env.call('); 93 | builder.append(JSON.stringify(node.name)); 94 | builder.append(', '); 95 | builder.append(args.join(', ')); 96 | builder.append(');\n'); 97 | }); 98 | } 99 | 100 | public addSequence(name: string, config: ParserSequenceConfig, compiler: SequenceCompiler): void { 101 | this.sequences[name] = compiler; 102 | this.parserSequences[name] = config; 103 | } 104 | 105 | public addRawFunction(name: string, compiler: RawFunctionCompiler): void { 106 | this.rawFunctions[name] = compiler; 107 | } 108 | 109 | public addDefaults(): void { 110 | this.addPlugin(ComponentsCompilerPlugin); 111 | this.addPlugin(ConditionalsCompilerPlugin); 112 | this.addPlugin(IncludesCompilerPlugin); 113 | this.addPlugin(JsBlockCompilerPlugin); 114 | this.addPlugin(JsonCompilerPlugin); 115 | this.addPlugin(LayoutCompilerPlugin); 116 | this.addPlugin(LoopsCompilerPlugin); 117 | this.addPlugin(StacksCompilerPlugin); 118 | } 119 | 120 | public compile(source: Source): string { 121 | this.builder = createStringBuilder(); 122 | this.footerBuilder = createStringBuilder(); 123 | 124 | const scanner = new Scanner(source); 125 | const lexer = createLexer(scanner, { 126 | rawFunctions: Object.keys(this.rawFunctions), 127 | }); 128 | const node = parse(lexer, { 129 | sequences: this.parserSequences, 130 | }); 131 | 132 | this.builder.append('(async function *(__env) {\n'); 133 | this.builder.append('with (__env.params) {'); 134 | this.compileNode(node); 135 | this.footerBuilder.append('}})'); 136 | 137 | return transpile( 138 | this.builder.build() + this.footerBuilder.build(), 139 | { 140 | transforms: { 141 | generator: false, 142 | stripWithFunctional: true, 143 | }, 144 | }, 145 | ).replace('var _c=_vm._c;', 'var _vm = __env.params;'); 146 | } 147 | 148 | protected compileContainer(node: NodeContainer): void { 149 | for (const sub of node.children) { 150 | this.compileNode(sub); 151 | } 152 | } 153 | 154 | protected compileComment(node: NodeComment): void { 155 | // TODO: emitComments config 156 | this.builder.append('/*'); 157 | this.builder.append(node.value.replace('*/', '* /')); 158 | this.builder.append('*/\n'); 159 | } 160 | 161 | protected compileText(node: NodeText): void { 162 | this.builder.append('yield '); 163 | this.builder.append(JSON.stringify(node.value)); 164 | this.builder.append(';\n'); 165 | } 166 | 167 | protected compileData(node: NodeData): void { 168 | this.builder.append('yield* __env.print('); 169 | 170 | this.builder.append((node.filters || []).reduce((prev, filter) => { 171 | return `await __env.filter(${prev}, ${JSON.stringify(filter.name)}${['', ...filter.args].join(', ')})`; 172 | }, node.value)); 173 | 174 | this.builder.append(', '); 175 | this.builder.append(JSON.stringify(node.escaped)); 176 | this.builder.append(');\n'); 177 | } 178 | 179 | protected compileFunction(node: NodeFunction): void { 180 | const fn = this.functions[node.name]; 181 | 182 | if (fn === undefined) { 183 | const args = node.args || []; 184 | this.builder.append('yield* __env.call('); 185 | this.builder.append(JSON.stringify(node.name)); 186 | this.builder.append(', '); 187 | this.builder.append(args.join(', ')); 188 | this.builder.append(');\n'); 189 | } else { 190 | fn(this.delegate, node); 191 | this.builder.append('\n'); 192 | } 193 | } 194 | 195 | protected compileRawFunction(node: NodeRawFunction): void { 196 | const fn = this.rawFunctions[node.name]; 197 | 198 | if (fn === undefined) { 199 | throw this.error(`Unknown function ${node.name}`, node, { 200 | start: node.start.relative(1), 201 | end: node.start.relative(1 + node.name.length), 202 | }); 203 | } 204 | 205 | fn(this.delegate, node); 206 | this.builder.append('\n'); 207 | } 208 | 209 | protected compileSequence(node: NodeSequence): void { 210 | const name = 211 | node.data[0] 212 | ? node.data[0][0].name 213 | : node.ending.name 214 | ; 215 | const fn = this.sequences[name]; 216 | 217 | if (fn === undefined) { 218 | throw this.error(`Unknown function ${name}`, node, { 219 | start: node.start.relative(1), 220 | end: node.start.relative(1 + name.length), 221 | }); 222 | } 223 | 224 | fn(this.delegate, node); 225 | this.builder.append('\n'); 226 | } 227 | 228 | protected compileNode(node: Node): void { 229 | switch (node.type) { 230 | case 'container': 231 | this.compileContainer(node); 232 | break; 233 | case 'comment': 234 | this.compileComment(node); 235 | break; 236 | case 'text': 237 | this.compileText(node); 238 | break; 239 | case 'data': 240 | this.compileData(node); 241 | break; 242 | case 'function': 243 | this.compileFunction(node); 244 | break; 245 | case 'raw-function': 246 | this.compileRawFunction(node); 247 | break; 248 | case 'sequence': 249 | this.compileSequence(node); 250 | break; 251 | } 252 | } 253 | 254 | protected error( 255 | message: string, 256 | node: Node, 257 | position?: Partial, 258 | ): never { 259 | throw new CompilerError(node.start.source, { 260 | start: position && position.start || node.start, 261 | end: position && position.end || node.end, 262 | }, message); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /packages/bladejs/src/environment/DefaultEnvironment.ts: -------------------------------------------------------------------------------- 1 | import { Runtime } from '../runtime/Runtime'; 2 | import { htmlspecialchars } from '../util/htmlspecialchars'; 3 | import { Environment, EnvironmentLoop, TemplateParams } from './Environment'; 4 | 5 | type SectionInfo = { 6 | readonly renderer: (parent: AsyncIterable) => AsyncIterable; 7 | readonly isShow: boolean; 8 | }; 9 | 10 | type TemplateInterceptor = (str: string) => void; 11 | 12 | type ComponentInfo = { 13 | readonly name: string; 14 | readonly args?: TemplateParams; 15 | readonly slots: { 16 | [name: string]: string[]; 17 | }; 18 | }; 19 | 20 | export class DefaultEnvironment implements Environment { 21 | public readonly runtime: Runtime; 22 | public readonly params: TemplateParams; 23 | 24 | private readonly interceptorsStack: TemplateInterceptor[] = []; 25 | private readonly loopsStack: EnvironmentLoop[] = []; 26 | private readonly sections = new Map(); 27 | private readonly stacks = new Map AsyncIterable>>(); 28 | private readonly componentsStack: ComponentInfo[] = []; 29 | 30 | public constructor(runtime: Runtime, params: TemplateParams) { 31 | this.runtime = runtime; 32 | this.params = params; 33 | } 34 | 35 | public async *process(input: AsyncIterable): AsyncIterable { 36 | for await (const chunk of input) { 37 | if (this.interceptorsStack.length === 0) { 38 | yield chunk; 39 | } else { 40 | this.interceptorsStack[this.interceptorsStack.length - 1](chunk); 41 | } 42 | } 43 | } 44 | 45 | public async *print(text: any, escaped: boolean): AsyncIterable { 46 | if (text instanceof Array) { 47 | // TODO: Special type for this 48 | yield* text; 49 | return; 50 | } 51 | 52 | let result: string; 53 | 54 | switch (true) { 55 | case typeof text === 'string' || text instanceof String: 56 | result = text; 57 | break; 58 | case typeof text === 'number' || text instanceof Number: 59 | result = text.toString(); 60 | break; 61 | case typeof text === 'boolean' || text instanceof Boolean: 62 | result = text ? 'true' : 'false'; 63 | break; 64 | case typeof text === 'undefined': 65 | result = 'undefined'; 66 | break; 67 | case typeof text === 'symbol' || text instanceof Symbol: 68 | case typeof text === 'function' || text instanceof Function: 69 | result = text.toString(); 70 | break; 71 | case typeof text === 'object': 72 | default: 73 | result = JSON.stringify(text, undefined, ' '); 74 | break; 75 | } 76 | 77 | yield escaped ? htmlspecialchars(result) : result; 78 | } 79 | 80 | public filter(text: any, name: string, ...args: any[]): PromiseLike | string { 81 | const fn = this.runtime.getFilter(name); 82 | return fn(this, text, ...args); 83 | } 84 | 85 | public async* call(name: string, ...args: any[]): AsyncIterable { 86 | const fn = this.runtime.getFunction(name); 87 | yield* fn(this, ...args); 88 | } 89 | 90 | public async* extends(name: string): AsyncIterable { 91 | yield* this.runtime.renderInternal(name, this); 92 | } 93 | 94 | public async* section( 95 | name: string, 96 | renderer: ((parent: AsyncIterable) => AsyncIterable) | any, 97 | isShow: boolean, 98 | ): void | AsyncIterable { 99 | // TODO: append 100 | 101 | const print = this.print.bind(this); 102 | if (this.sections.has(name)) { 103 | const old = this.sections.get(name)!; 104 | 105 | this.sections.set(name, { 106 | renderer: typeof renderer === 'string' 107 | ? async function* () { 108 | yield* print(renderer, true); 109 | } 110 | : async function* () { 111 | yield* old.renderer(renderer()); 112 | } 113 | , 114 | isShow, 115 | }); 116 | } else { 117 | this.sections.set(name, { 118 | renderer: typeof renderer === 'function' 119 | ? renderer 120 | : async function* () { 121 | yield* print(renderer, true); 122 | }, 123 | isShow, 124 | }); 125 | } 126 | 127 | if (isShow) { 128 | yield* this.yield(name); 129 | } 130 | } 131 | 132 | public async* yield(name: string, def?: string): AsyncIterable { 133 | const section = this.sections.get(name); 134 | if (section === undefined) { 135 | if (def === undefined) { 136 | throw new Error(`Section '${name}' does not exist.`); 137 | } 138 | 139 | yield def; 140 | return ''; 141 | } 142 | 143 | yield* section.renderer((async function* (): AsyncIterable { 144 | if (def === undefined) { 145 | throw new Error(`Section '${name}' does not have parent.`); 146 | } 147 | 148 | yield def; 149 | })()); 150 | } 151 | 152 | public pushLoop(data: any) { 153 | let iteratee: ArrayLike; 154 | let keyRetriever: (index: any) => any; 155 | let valueRetriever: (index: any) => any; 156 | 157 | if ( 158 | data instanceof Array || 159 | data instanceof String || 160 | typeof data === 'string' 161 | ) { 162 | iteratee = data; 163 | keyRetriever = index => index; 164 | valueRetriever = index => data[index]; 165 | } else if (data instanceof Set) { 166 | iteratee = [...data.values()]; 167 | keyRetriever = index => index; 168 | valueRetriever = key => iteratee[key]; 169 | } else if (data instanceof Map) { 170 | iteratee = [...data.keys()]; 171 | keyRetriever = index => iteratee[index]; 172 | valueRetriever = key => data.get(key); 173 | } else if (typeof data === 'object' && data !== null) { 174 | iteratee = Object.keys(data); 175 | keyRetriever = index => iteratee[index]; 176 | valueRetriever = key => data[key]; 177 | } else { 178 | throw new TypeError(data + ' is not iterable'); 179 | } 180 | 181 | const { loopsStack } = this; 182 | const loop: EnvironmentLoop = { 183 | __hasRemaining: iteratee.length !== 0, 184 | __key: null, 185 | __value: null, 186 | 187 | iteration: 0, 188 | index: 0, 189 | remaining: iteratee.length, 190 | count: iteratee.length, 191 | first: true, 192 | last: iteratee.length === 1, 193 | odd: false, 194 | even: true, 195 | depth: loopsStack.length + 1, 196 | parent: loopsStack.length === 0 ? undefined : loopsStack[loopsStack.length - 1], 197 | 198 | __next() { 199 | this.index = this.iteration; 200 | ++this.iteration; 201 | 202 | this.__hasRemaining = this.index !== this.count; 203 | this.__key = keyRetriever(this.index); 204 | this.__value = valueRetriever(this.__key); 205 | 206 | this.remaining = this.count - this.iteration; 207 | this.first = this.iteration === 1; 208 | this.last = this.index + 1 === iteratee.length; 209 | 210 | this.odd = !this.odd; 211 | this.even = !this.even; 212 | }, 213 | }; 214 | 215 | loop.__next(); 216 | loopsStack.push(loop); 217 | 218 | return loop; 219 | } 220 | 221 | public popLoop() { 222 | this.loopsStack.pop(); 223 | } 224 | 225 | public push(name: string, renderer: () => AsyncIterable, prepend: boolean = false): void { 226 | let stack = this.stacks.get(name); 227 | if (stack === undefined) { 228 | stack = []; 229 | this.stacks.set(name, stack); 230 | } 231 | 232 | if (prepend) { 233 | stack.unshift(renderer); 234 | } else { 235 | stack.push(renderer); 236 | } 237 | } 238 | 239 | public async *stack(name: string): AsyncIterable { 240 | const stack = this.stacks.get(name); 241 | if (stack === undefined) { 242 | return; 243 | } 244 | 245 | for (const item of stack) { 246 | yield* item(); 247 | } 248 | } 249 | 250 | public beginComponent(name: string, args?: TemplateParams): void { 251 | const component: ComponentInfo = { 252 | name, 253 | args, 254 | slots: {}, 255 | }; 256 | this.componentsStack.push(component); 257 | 258 | const slot: string[] = []; 259 | component.slots['slot'] = slot; 260 | 261 | this.interceptorsStack.push(str => { 262 | slot.push(str); 263 | }); 264 | } 265 | 266 | public async *endComponent(): AsyncIterable { 267 | const component = this.componentsStack.pop(); 268 | if (component === undefined) { 269 | throw new Error('Unexpected endComponent call'); 270 | } 271 | 272 | this.interceptorsStack.pop(); 273 | 274 | yield* this.runtime.render(component.name, { 275 | ...component.args, 276 | ...component.slots, 277 | }); 278 | } 279 | 280 | public beginSlot(name: string): void { 281 | if (this.componentsStack.length === 0) { 282 | throw new Error('Unexpected beginSlot call'); 283 | } 284 | 285 | const component = this.componentsStack[this.componentsStack.length - 1]; 286 | const slot: string[] = component.slots[name] || []; 287 | component.slots[name] = slot; 288 | 289 | this.interceptorsStack.push(str => { 290 | slot.push(str); 291 | }); 292 | } 293 | 294 | public endSlot(): void { 295 | if (this.componentsStack.length === 0) { 296 | throw new Error('Unexpected endSlot call'); 297 | } 298 | 299 | this.interceptorsStack.pop(); 300 | // TODO: Check this? 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /packages/bladejs-compiler/src/lexer/Lexer.ts: -------------------------------------------------------------------------------- 1 | import { LexerError } from '../error/LexerError'; 2 | import { Scanner } from '../scanner/Scanner'; 3 | import { Location } from '../source/Location'; 4 | import { Position } from '../source/Position'; 5 | import { Source } from '../source/Source'; 6 | import { Char } from '../string/Char'; 7 | import { FullToken, TokenData } from './Token'; 8 | 9 | export interface Lexer { 10 | readonly source: Source; 11 | readonly position: Position; 12 | 13 | peek(): FullToken; 14 | 15 | next(): FullToken; 16 | 17 | eof(): boolean; 18 | } 19 | 20 | export interface LexerConfig { 21 | readonly rawFunctions: ReadonlyArray; 22 | } 23 | 24 | export const DEFAULT_LEXER_CONFIG: LexerConfig = { 25 | rawFunctions: ['js'], 26 | }; 27 | 28 | export const createLexer = (input: Scanner, lexerConfig?: Partial): Lexer => { 29 | const config: LexerConfig = { 30 | ...DEFAULT_LEXER_CONFIG, 31 | ...lexerConfig, 32 | }; 33 | 34 | let text = ''; 35 | let isRaw = false; 36 | let rawFunction: string | undefined; 37 | let rawArgs: string[] | undefined; 38 | 39 | let prevPosition = input.position; 40 | 41 | const error = (message: string, position: Position = input.position): never => { 42 | throw new LexerError(input.source, { 43 | start: position, 44 | end: position, 45 | }, message); 46 | }; 47 | 48 | const tokenPosition = (end: Position = input.position): Location => { 49 | const start = prevPosition; 50 | prevPosition = end; 51 | 52 | return { start, end }; 53 | }; 54 | 55 | const flush = function *(token?: () => FullToken, begin?: Position): IterableIterator { 56 | if (text !== '') { 57 | yield { 58 | ...tokenPosition(begin), 59 | type: 'text', 60 | value: text, 61 | }; 62 | text = ''; 63 | } 64 | if (token !== undefined) { 65 | yield token(); 66 | } 67 | }; 68 | 69 | const parseStringUntil = ( 70 | offset: number, 71 | start: number, 72 | data: string, 73 | delimiters: T[], 74 | ): [T | undefined, string, number | undefined] => { 75 | let result = ''; 76 | let escape = false; 77 | let quote: string | undefined; 78 | const braces: string[] = []; 79 | 80 | for (let i = start; i < data.length; ++i) { 81 | const c = data[i]; 82 | 83 | if (quote !== undefined) { 84 | if (escape) { 85 | result += c; 86 | escape = false; 87 | continue; 88 | } 89 | 90 | if (c === '\\') { 91 | escape = true; 92 | continue; 93 | } 94 | 95 | result += c; 96 | if (quote === c) { 97 | quote = undefined; 98 | } 99 | continue; 100 | } 101 | 102 | if (braces.length === 0 && delimiters.indexOf(c as T) !== -1) { 103 | return [c as T, result, i + 1]; 104 | } 105 | 106 | switch (c) { 107 | case '\'': 108 | case '"': 109 | case '`': 110 | result += c; 111 | quote = c; 112 | break; 113 | case '(': 114 | result += c; 115 | braces.push(')'); 116 | break; 117 | case '{': 118 | result += c; 119 | braces.push('}'); 120 | break; 121 | case '[': 122 | result += c; 123 | braces.push(']'); 124 | break; 125 | case ')': 126 | case '}': 127 | case ']': 128 | result += c; 129 | 130 | const expected = braces.pop(); 131 | if (expected !== c) { 132 | error( 133 | expected 134 | ? `expected ${JSON.stringify(expected)}, got ${JSON.stringify(c)}` 135 | : `unexpected ${JSON.stringify(c)}` 136 | , 137 | input.position.relative(offset + - data.length + i), 138 | ); 139 | } 140 | break; 141 | default: 142 | result += c; 143 | break; 144 | } 145 | } 146 | 147 | return [undefined, result, undefined]; 148 | }; 149 | 150 | const parseFilters = (offset: number, data: string): Pick => { 151 | let position: number = 0; 152 | let value: string; 153 | { 154 | const [delimiter, newValue, newPosition] = parseStringUntil(offset, position, data, ['|']); 155 | value = newValue.trim(); 156 | if (newPosition === undefined || delimiter === undefined) { 157 | return { 158 | value, 159 | }; 160 | } 161 | position = newPosition; 162 | } 163 | 164 | const filters: Array[0]> = []; 165 | let filterName: string | undefined; 166 | let args: string[] = []; 167 | 168 | while (data !== '') { 169 | const [delimiter, item, newPosition] = parseStringUntil( 170 | offset, 171 | position, 172 | data, 173 | [filterName === undefined ? ':' : ',', '|'], 174 | ); 175 | 176 | if (filterName === undefined) { 177 | filterName = item.trim(); 178 | } else { 179 | args.push(item.trim()); 180 | } 181 | 182 | if (delimiter === '|') { 183 | filters.push({ 184 | name: filterName, 185 | args, 186 | }); 187 | 188 | filterName = undefined; 189 | args = []; 190 | } 191 | 192 | if (newPosition === undefined || delimiter === undefined) { 193 | break; 194 | } 195 | 196 | position = newPosition; 197 | } 198 | 199 | if (filterName !== undefined) { 200 | filters.push({ 201 | name: filterName, 202 | args, 203 | }); 204 | } 205 | 206 | return { 207 | value, 208 | filters, 209 | }; 210 | }; 211 | 212 | const generator = function *(): IterableIterator { 213 | while (!input.eof()) { 214 | const c = input.peek(); 215 | 216 | if (isRaw && c !== '@') { 217 | text += c; 218 | input.skip(); 219 | continue; 220 | } 221 | 222 | switch (c) { 223 | case '{': { 224 | if (input.sub(2, 1) === '!!') { 225 | // unescaped data 226 | 227 | const position = input.position; 228 | input.skip(3); 229 | 230 | let data = ''; 231 | while (!input.eof()) { 232 | if (input.sub(3) === '!!}') { 233 | input.skip(3); 234 | break; 235 | } else { 236 | data += input.next(); 237 | } 238 | } 239 | 240 | yield* flush(() => ({ 241 | ...tokenPosition(), 242 | type: 'data', 243 | escaped: false, 244 | ...parseFilters(-3, data), 245 | }), position); 246 | 247 | continue; 248 | } else if (input.peek(1) === '{') { 249 | if (input.sub(2, 2) === '--') { 250 | // comment 251 | 252 | const position = input.position; 253 | input.skip(4); 254 | 255 | let comment = ''; 256 | while (!input.eof()) { 257 | if (input.sub(4) === '--}}') { 258 | input.skip(4); 259 | break; 260 | } else { 261 | comment += input.next(); 262 | } 263 | } 264 | 265 | yield* flush(() => ({ 266 | ...tokenPosition(), 267 | type: 'comment', 268 | value: comment, 269 | }), position); 270 | } else { 271 | // escaped data 272 | 273 | const position = input.position; 274 | input.skip(2); 275 | 276 | let data = ''; 277 | while (!input.eof()) { 278 | if (input.sub(2) === '}}') { 279 | input.skip(2); 280 | break; 281 | } else { 282 | data += input.next(); 283 | } 284 | } 285 | 286 | yield* flush(() => ({ 287 | ...tokenPosition(), 288 | type: 'data', 289 | escaped: true, 290 | ...parseFilters(-2, data), 291 | }), position); 292 | } 293 | 294 | continue; 295 | } 296 | break; 297 | } 298 | case '@': { 299 | const position = input.position; 300 | input.skip(); 301 | 302 | if (!Char.isLetter(input.peek())) { 303 | if (isRaw) { 304 | text += '@'; 305 | continue; 306 | } 307 | 308 | while (!input.eof() && !Char.isWhitespace(input.peek())) { 309 | text += input.next(); 310 | } 311 | continue; 312 | } 313 | 314 | let name = ''; 315 | while (Char.isLetter(input.peek())) { 316 | name += input.next(); 317 | } 318 | 319 | if (isRaw) { 320 | if (rawFunction && name === 'end' + rawFunction) { 321 | isRaw = false; 322 | 323 | // verbatim is always treated as text 324 | if (rawFunction === 'verbatim') { 325 | yield* flush(); 326 | } else { 327 | if (rawArgs !== undefined) { 328 | yield { 329 | ...tokenPosition(input.position), 330 | type: 'raw-function', 331 | name: rawFunction, 332 | args: rawArgs, 333 | content: text, 334 | }; 335 | } else { 336 | yield { 337 | ...tokenPosition(input.position), 338 | type: 'raw-function', 339 | name: rawFunction, 340 | content: text, 341 | }; 342 | } 343 | text = ''; 344 | } 345 | 346 | rawFunction = undefined; 347 | rawArgs = undefined; 348 | continue; 349 | } 350 | 351 | text += '@'; 352 | text += name; 353 | continue; 354 | } 355 | 356 | // @verbatim may not have arguments 357 | if (name === 'verbatim') { 358 | yield* flush(); 359 | isRaw = true; 360 | rawFunction = name; 361 | continue; 362 | } 363 | 364 | let i = 0; 365 | while (Char.isWhitespace(input.peek(i))) { 366 | ++i; 367 | } 368 | 369 | if (input.peek(i) === '(') { 370 | input.skip(i + 1); 371 | 372 | const args: string[] = []; 373 | const braces: string[] = [')']; 374 | let ch: string; 375 | let arg = ''; 376 | while ((ch = input.next()) !== ')' || braces.length !== 1) { 377 | switch (ch) { 378 | case '\'': 379 | case '"': 380 | case '`': { 381 | let escape = false; 382 | let ch2: string; 383 | 384 | arg += ch; 385 | while ((ch2 = input.next()) !== ch || escape) { 386 | if (ch2 === '\\') { 387 | escape = !escape; 388 | } else { 389 | escape = false; 390 | } 391 | 392 | arg += ch2; 393 | } 394 | arg += ch2; 395 | break; 396 | } 397 | case ',': 398 | if (braces.length === 1) { 399 | args.push(arg.trim()); 400 | arg = ''; 401 | } else { 402 | arg += ch; 403 | } 404 | break; 405 | case '(': 406 | arg += ch; 407 | braces.push(')'); 408 | break; 409 | case '{': 410 | arg += ch; 411 | braces.push('}'); 412 | break; 413 | case '[': 414 | arg += ch; 415 | braces.push(']'); 416 | break; 417 | case ')': 418 | case '}': 419 | case ']': 420 | arg += ch; 421 | 422 | const expected = braces.pop(); 423 | if (expected !== ch) { 424 | error( 425 | `expected ${JSON.stringify(expected)}, got ${JSON.stringify(ch)}`, 426 | input.position.relative(-1), 427 | ); 428 | } 429 | break; 430 | default: 431 | arg += ch; 432 | break; 433 | } 434 | } 435 | arg = arg.trim(); 436 | if (arg !== '') { 437 | args.push(arg); 438 | } 439 | 440 | if (config.rawFunctions.indexOf(name) !== -1) { 441 | yield* flush(); 442 | isRaw = true; 443 | rawFunction = name; 444 | rawArgs = args; 445 | continue; 446 | } 447 | 448 | yield* flush(() => ({ 449 | ...tokenPosition(), 450 | type: 'function', 451 | name, 452 | args, 453 | }), position); 454 | } else { 455 | if (config.rawFunctions.indexOf(name) !== -1) { 456 | yield* flush(); 457 | isRaw = true; 458 | rawFunction = name; 459 | rawArgs = undefined; 460 | continue; 461 | } 462 | 463 | yield* flush(() => ({ 464 | ...tokenPosition(), 465 | type: 'function', 466 | name, 467 | }), position); 468 | } 469 | 470 | continue; 471 | } 472 | } 473 | 474 | input.next(); 475 | text += c; 476 | } 477 | 478 | yield* flush(); 479 | 480 | const eof: FullToken = { ...tokenPosition(), type: 'eof' }; 481 | while (true) { 482 | yield eof; 483 | } 484 | }; 485 | 486 | const tokens = generator(); 487 | 488 | let current: FullToken | undefined; 489 | 490 | const peek = (): FullToken => { 491 | if (current === undefined) { 492 | return current = tokens.next().value; 493 | } 494 | 495 | return current; 496 | }; 497 | 498 | const next = (): FullToken => { 499 | const tok = peek(); 500 | current = undefined; 501 | return tok; 502 | }; 503 | 504 | return { 505 | source: input.source, 506 | get position(): Position { 507 | if (current !== undefined) { 508 | return current.start; 509 | } else { 510 | return input.position; 511 | } 512 | }, 513 | 514 | peek, 515 | next, 516 | eof: () => input.eof(), 517 | }; 518 | }; 519 | -------------------------------------------------------------------------------- /packages/bladejs-loader/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.8.3" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" 8 | integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== 9 | dependencies: 10 | "@babel/highlight" "^7.8.3" 11 | 12 | "@babel/highlight@^7.8.3": 13 | version "7.8.3" 14 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" 15 | integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== 16 | dependencies: 17 | chalk "^2.0.0" 18 | esutils "^2.0.2" 19 | js-tokens "^4.0.0" 20 | 21 | "@types/anymatch@*": 22 | version "1.3.1" 23 | resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" 24 | integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== 25 | 26 | "@types/loader-utils@^1.1.3": 27 | version "1.1.3" 28 | resolved "https://registry.yarnpkg.com/@types/loader-utils/-/loader-utils-1.1.3.tgz#82b9163f2ead596c68a8c03e450fbd6e089df401" 29 | integrity sha512-euKGFr2oCB3ASBwG39CYJMR3N9T0nanVqXdiH7Zu/Nqddt6SmFRxytq/i2w9LQYNQekEtGBz+pE3qG6fQTNvRg== 30 | dependencies: 31 | "@types/node" "*" 32 | "@types/webpack" "*" 33 | 34 | "@types/node@*": 35 | version "13.5.3" 36 | resolved "https://registry.yarnpkg.com/@types/node/-/node-13.5.3.tgz#37f1f539b7535b9fb4ef77d59db1847a837b7f17" 37 | integrity sha512-ZPnWX9PW992w6DUsz3JIXHaSb5v7qmKCVzC3km6SxcDGxk7zmLfYaCJTbktIa5NeywJkkZDhGldKqDIvC5DRrA== 38 | 39 | "@types/node@^12.6.3": 40 | version "12.12.26" 41 | resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9" 42 | integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA== 43 | 44 | "@types/schema-utils@^1.0.0": 45 | version "1.0.0" 46 | resolved "https://registry.yarnpkg.com/@types/schema-utils/-/schema-utils-1.0.0.tgz#295d36f01e2cb8bc3207ca1d9a68e210db6b40cb" 47 | integrity sha512-YesPanU1+WCigC/Aj1Mga8UCOjHIfMNHZ3zzDsUY7lI8GlKnh/Kv2QwJOQ+jNQ36Ru7IfzSedlG14hppYaN13A== 48 | 49 | "@types/source-list-map@*": 50 | version "0.1.2" 51 | resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" 52 | integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== 53 | 54 | "@types/tapable@*": 55 | version "1.0.5" 56 | resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" 57 | integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== 58 | 59 | "@types/uglify-js@*": 60 | version "3.0.4" 61 | resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082" 62 | integrity sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ== 63 | dependencies: 64 | source-map "^0.6.1" 65 | 66 | "@types/webpack-sources@*": 67 | version "0.1.6" 68 | resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.6.tgz#3d21dfc2ec0ad0c77758e79362426a9ba7d7cbcb" 69 | integrity sha512-FtAWR7wR5ocJ9+nP137DV81tveD/ZgB1sadnJ/axUGM3BUVfRPx8oQNMtv3JNfTeHx3VP7cXiyfR/jmtEsVHsQ== 70 | dependencies: 71 | "@types/node" "*" 72 | "@types/source-list-map" "*" 73 | source-map "^0.6.1" 74 | 75 | "@types/webpack@*": 76 | version "4.41.3" 77 | resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.3.tgz#30c2251db1d69a45bbffd79c0577dd9baf50e7ba" 78 | integrity sha512-dH+BZ6pHBZFrXpnif0YU/PbmUq3lQrvRPnqkxsciSIzvG/DE+Vm/Wrjn56T7V3+B5ryQa5fw0oGnHL8tk4ll6w== 79 | dependencies: 80 | "@types/anymatch" "*" 81 | "@types/node" "*" 82 | "@types/tapable" "*" 83 | "@types/uglify-js" "*" 84 | "@types/webpack-sources" "*" 85 | source-map "^0.6.0" 86 | 87 | ajv-errors@^1.0.0: 88 | version "1.0.1" 89 | resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" 90 | integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== 91 | 92 | ajv-keywords@^3.1.0: 93 | version "3.4.1" 94 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" 95 | integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== 96 | 97 | ajv@^6.1.0: 98 | version "6.11.0" 99 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" 100 | integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA== 101 | dependencies: 102 | fast-deep-equal "^3.1.1" 103 | fast-json-stable-stringify "^2.0.0" 104 | json-schema-traverse "^0.4.1" 105 | uri-js "^4.2.2" 106 | 107 | ansi-styles@^3.2.1: 108 | version "3.2.1" 109 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 110 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 111 | dependencies: 112 | color-convert "^1.9.0" 113 | 114 | arg@^4.1.0: 115 | version "4.1.2" 116 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.2.tgz#e70c90579e02c63d80e3ad4e31d8bfdb8bd50064" 117 | integrity sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg== 118 | 119 | argparse@^1.0.7: 120 | version "1.0.10" 121 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 122 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 123 | dependencies: 124 | sprintf-js "~1.0.2" 125 | 126 | balanced-match@^1.0.0: 127 | version "1.0.0" 128 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 129 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 130 | 131 | big.js@^5.2.2: 132 | version "5.2.2" 133 | resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" 134 | integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== 135 | 136 | brace-expansion@^1.1.7: 137 | version "1.1.11" 138 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 139 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 140 | dependencies: 141 | balanced-match "^1.0.0" 142 | concat-map "0.0.1" 143 | 144 | buffer-from@^1.0.0: 145 | version "1.1.1" 146 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 147 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 148 | 149 | builtin-modules@^1.1.1: 150 | version "1.1.1" 151 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 152 | integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= 153 | 154 | chalk@^2.0.0, chalk@^2.3.0: 155 | version "2.4.2" 156 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 157 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 158 | dependencies: 159 | ansi-styles "^3.2.1" 160 | escape-string-regexp "^1.0.5" 161 | supports-color "^5.3.0" 162 | 163 | color-convert@^1.9.0: 164 | version "1.9.3" 165 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 166 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 167 | dependencies: 168 | color-name "1.1.3" 169 | 170 | color-name@1.1.3: 171 | version "1.1.3" 172 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 173 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 174 | 175 | commander@^2.12.1: 176 | version "2.20.3" 177 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" 178 | integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 179 | 180 | concat-map@0.0.1: 181 | version "0.0.1" 182 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 183 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 184 | 185 | diff@^4.0.1: 186 | version "4.0.2" 187 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 188 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 189 | 190 | emojis-list@^2.0.0: 191 | version "2.1.0" 192 | resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" 193 | integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= 194 | 195 | escape-string-regexp@^1.0.5: 196 | version "1.0.5" 197 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 198 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 199 | 200 | esprima@^4.0.0: 201 | version "4.0.1" 202 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 203 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 204 | 205 | esutils@^2.0.2: 206 | version "2.0.3" 207 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 208 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 209 | 210 | fast-deep-equal@^3.1.1: 211 | version "3.1.1" 212 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" 213 | integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== 214 | 215 | fast-json-stable-stringify@^2.0.0: 216 | version "2.1.0" 217 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" 218 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 219 | 220 | fs.realpath@^1.0.0: 221 | version "1.0.0" 222 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 223 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 224 | 225 | glob@^7.1.1: 226 | version "7.1.6" 227 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 228 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 229 | dependencies: 230 | fs.realpath "^1.0.0" 231 | inflight "^1.0.4" 232 | inherits "2" 233 | minimatch "^3.0.4" 234 | once "^1.3.0" 235 | path-is-absolute "^1.0.0" 236 | 237 | has-flag@^3.0.0: 238 | version "3.0.0" 239 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 240 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 241 | 242 | inflight@^1.0.4: 243 | version "1.0.6" 244 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 245 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 246 | dependencies: 247 | once "^1.3.0" 248 | wrappy "1" 249 | 250 | inherits@2: 251 | version "2.0.4" 252 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 253 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 254 | 255 | js-tokens@^4.0.0: 256 | version "4.0.0" 257 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 258 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 259 | 260 | js-yaml@^3.13.1: 261 | version "3.13.1" 262 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 263 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 264 | dependencies: 265 | argparse "^1.0.7" 266 | esprima "^4.0.0" 267 | 268 | json-schema-traverse@^0.4.1: 269 | version "0.4.1" 270 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" 271 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 272 | 273 | json5@^1.0.1: 274 | version "1.0.1" 275 | resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" 276 | integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== 277 | dependencies: 278 | minimist "^1.2.0" 279 | 280 | loader-utils@^1.2.3: 281 | version "1.2.3" 282 | resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" 283 | integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== 284 | dependencies: 285 | big.js "^5.2.2" 286 | emojis-list "^2.0.0" 287 | json5 "^1.0.1" 288 | 289 | make-error@^1.1.1: 290 | version "1.3.5" 291 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" 292 | integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== 293 | 294 | minimatch@^3.0.4: 295 | version "3.0.4" 296 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 297 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 298 | dependencies: 299 | brace-expansion "^1.1.7" 300 | 301 | minimist@0.0.8: 302 | version "0.0.8" 303 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 304 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 305 | 306 | minimist@^1.2.0: 307 | version "1.2.0" 308 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" 309 | integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= 310 | 311 | mkdirp@^0.5.1: 312 | version "0.5.1" 313 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 314 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 315 | dependencies: 316 | minimist "0.0.8" 317 | 318 | once@^1.3.0: 319 | version "1.4.0" 320 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 321 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 322 | dependencies: 323 | wrappy "1" 324 | 325 | path-is-absolute@^1.0.0: 326 | version "1.0.1" 327 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 328 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 329 | 330 | path-parse@^1.0.6: 331 | version "1.0.6" 332 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 333 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 334 | 335 | punycode@^2.1.0: 336 | version "2.1.1" 337 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" 338 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== 339 | 340 | resolve@^1.3.2: 341 | version "1.15.0" 342 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" 343 | integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== 344 | dependencies: 345 | path-parse "^1.0.6" 346 | 347 | schema-utils@^1.0.0: 348 | version "1.0.0" 349 | resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" 350 | integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== 351 | dependencies: 352 | ajv "^6.1.0" 353 | ajv-errors "^1.0.0" 354 | ajv-keywords "^3.1.0" 355 | 356 | semver@^5.3.0: 357 | version "5.7.1" 358 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 359 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 360 | 361 | source-map-support@^0.5.6: 362 | version "0.5.16" 363 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" 364 | integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== 365 | dependencies: 366 | buffer-from "^1.0.0" 367 | source-map "^0.6.0" 368 | 369 | source-map@^0.6.0, source-map@^0.6.1: 370 | version "0.6.1" 371 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 372 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 373 | 374 | sprintf-js@~1.0.2: 375 | version "1.0.3" 376 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 377 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 378 | 379 | supports-color@^5.3.0: 380 | version "5.5.0" 381 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 382 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 383 | dependencies: 384 | has-flag "^3.0.0" 385 | 386 | ts-node@^8.3.0: 387 | version "8.6.2" 388 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" 389 | integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== 390 | dependencies: 391 | arg "^4.1.0" 392 | diff "^4.0.1" 393 | make-error "^1.1.1" 394 | source-map-support "^0.5.6" 395 | yn "3.1.1" 396 | 397 | tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1: 398 | version "1.10.0" 399 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" 400 | integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== 401 | 402 | tslint@^5.18.0: 403 | version "5.20.1" 404 | resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" 405 | integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== 406 | dependencies: 407 | "@babel/code-frame" "^7.0.0" 408 | builtin-modules "^1.1.1" 409 | chalk "^2.3.0" 410 | commander "^2.12.1" 411 | diff "^4.0.1" 412 | glob "^7.1.1" 413 | js-yaml "^3.13.1" 414 | minimatch "^3.0.4" 415 | mkdirp "^0.5.1" 416 | resolve "^1.3.2" 417 | semver "^5.3.0" 418 | tslib "^1.8.0" 419 | tsutils "^2.29.0" 420 | 421 | tsutils@^2.29.0: 422 | version "2.29.0" 423 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" 424 | integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== 425 | dependencies: 426 | tslib "^1.8.1" 427 | 428 | typescript@^3.5.3: 429 | version "3.7.5" 430 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" 431 | integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== 432 | 433 | uri-js@^4.2.2: 434 | version "4.2.2" 435 | resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" 436 | integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== 437 | dependencies: 438 | punycode "^2.1.0" 439 | 440 | wrappy@1: 441 | version "1.0.2" 442 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 443 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 444 | 445 | yn@3.1.1: 446 | version "3.1.1" 447 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 448 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 449 | -------------------------------------------------------------------------------- /packages/bladejs/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@babel/code-frame@^7.0.0": 6 | version "7.8.3" 7 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" 8 | integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== 9 | dependencies: 10 | "@babel/highlight" "^7.8.3" 11 | 12 | "@babel/highlight@^7.8.3": 13 | version "7.8.3" 14 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" 15 | integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== 16 | dependencies: 17 | chalk "^2.0.0" 18 | esutils "^2.0.2" 19 | js-tokens "^4.0.0" 20 | 21 | "@types/chai@^4.1.7": 22 | version "4.2.8" 23 | resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.8.tgz#c8d645506db0d15f4aafd4dfa873f443ad87ea59" 24 | integrity sha512-U1bQiWbln41Yo6EeHMr+34aUhvrMVyrhn9lYfPSpLTCrZlGxU4Rtn1bocX+0p2Fc/Jkd2FanCEXdw0WNfHHM0w== 25 | 26 | "@types/mocha@^5.2.7": 27 | version "5.2.7" 28 | resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" 29 | integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== 30 | 31 | "@types/node@^12.6.3": 32 | version "12.12.26" 33 | resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.26.tgz#213e153babac0ed169d44a6d919501e68f59dea9" 34 | integrity sha512-UmUm94/QZvU5xLcUlNR8hA7Ac+fGpO1EG/a8bcWVz0P0LqtxFmun9Y2bbtuckwGboWJIT70DoWq1r3hb56n3DA== 35 | 36 | ansi-colors@3.2.3: 37 | version "3.2.3" 38 | resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" 39 | integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== 40 | 41 | ansi-regex@^3.0.0: 42 | version "3.0.0" 43 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 44 | integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 45 | 46 | ansi-regex@^4.1.0: 47 | version "4.1.0" 48 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" 49 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== 50 | 51 | ansi-styles@^3.2.0, ansi-styles@^3.2.1: 52 | version "3.2.1" 53 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 54 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== 55 | dependencies: 56 | color-convert "^1.9.0" 57 | 58 | arg@^4.1.0: 59 | version "4.1.2" 60 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.2.tgz#e70c90579e02c63d80e3ad4e31d8bfdb8bd50064" 61 | integrity sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg== 62 | 63 | argparse@^1.0.7: 64 | version "1.0.10" 65 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 66 | integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== 67 | dependencies: 68 | sprintf-js "~1.0.2" 69 | 70 | assertion-error@^1.1.0: 71 | version "1.1.0" 72 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 73 | integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== 74 | 75 | balanced-match@^1.0.0: 76 | version "1.0.0" 77 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 78 | integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 79 | 80 | brace-expansion@^1.1.7: 81 | version "1.1.11" 82 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 83 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 84 | dependencies: 85 | balanced-match "^1.0.0" 86 | concat-map "0.0.1" 87 | 88 | browser-stdout@1.3.1: 89 | version "1.3.1" 90 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 91 | integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== 92 | 93 | buffer-from@^1.0.0: 94 | version "1.1.1" 95 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 96 | integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 97 | 98 | builtin-modules@^1.1.1: 99 | version "1.1.1" 100 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" 101 | integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= 102 | 103 | camelcase@^5.0.0: 104 | version "5.3.1" 105 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" 106 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== 107 | 108 | chai-exclude@^2.0.1: 109 | version "2.0.2" 110 | resolved "https://registry.yarnpkg.com/chai-exclude/-/chai-exclude-2.0.2.tgz#8f2f2881ee8f3ddf4c5af0f949c063503eee30f8" 111 | integrity sha512-QmNVnvdSw8Huccdjm49mKu3HtoHxvjdavgYkY0KPQ5MI5UWfbc9sX1YqRgaMPf2GGtDXPoF2ram3AeNS4945Xw== 112 | dependencies: 113 | fclone "^1.0.11" 114 | 115 | chai@^4.2.0: 116 | version "4.2.0" 117 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" 118 | integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== 119 | dependencies: 120 | assertion-error "^1.1.0" 121 | check-error "^1.0.2" 122 | deep-eql "^3.0.1" 123 | get-func-name "^2.0.0" 124 | pathval "^1.1.0" 125 | type-detect "^4.0.5" 126 | 127 | chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0: 128 | version "2.4.2" 129 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 130 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 131 | dependencies: 132 | ansi-styles "^3.2.1" 133 | escape-string-regexp "^1.0.5" 134 | supports-color "^5.3.0" 135 | 136 | check-error@^1.0.2: 137 | version "1.0.2" 138 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 139 | integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= 140 | 141 | cliui@^5.0.0: 142 | version "5.0.0" 143 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" 144 | integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== 145 | dependencies: 146 | string-width "^3.1.0" 147 | strip-ansi "^5.2.0" 148 | wrap-ansi "^5.1.0" 149 | 150 | color-convert@^1.9.0: 151 | version "1.9.3" 152 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" 153 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== 154 | dependencies: 155 | color-name "1.1.3" 156 | 157 | color-name@1.1.3: 158 | version "1.1.3" 159 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 160 | integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= 161 | 162 | commander@^2.12.1: 163 | version "2.20.3" 164 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" 165 | integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 166 | 167 | concat-map@0.0.1: 168 | version "0.0.1" 169 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 170 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 171 | 172 | debug@3.2.6: 173 | version "3.2.6" 174 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 175 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 176 | dependencies: 177 | ms "^2.1.1" 178 | 179 | decamelize@^1.2.0: 180 | version "1.2.0" 181 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 182 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= 183 | 184 | deep-eql@^3.0.1: 185 | version "3.0.1" 186 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" 187 | integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== 188 | dependencies: 189 | type-detect "^4.0.0" 190 | 191 | define-properties@^1.1.2, define-properties@^1.1.3: 192 | version "1.1.3" 193 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" 194 | integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 195 | dependencies: 196 | object-keys "^1.0.12" 197 | 198 | diff@3.5.0: 199 | version "3.5.0" 200 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 201 | integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== 202 | 203 | diff@^4.0.1: 204 | version "4.0.2" 205 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 206 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 207 | 208 | emoji-regex@^7.0.1: 209 | version "7.0.3" 210 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" 211 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== 212 | 213 | es-abstract@^1.17.0-next.1: 214 | version "1.17.4" 215 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" 216 | integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== 217 | dependencies: 218 | es-to-primitive "^1.2.1" 219 | function-bind "^1.1.1" 220 | has "^1.0.3" 221 | has-symbols "^1.0.1" 222 | is-callable "^1.1.5" 223 | is-regex "^1.0.5" 224 | object-inspect "^1.7.0" 225 | object-keys "^1.1.1" 226 | object.assign "^4.1.0" 227 | string.prototype.trimleft "^2.1.1" 228 | string.prototype.trimright "^2.1.1" 229 | 230 | es-to-primitive@^1.2.1: 231 | version "1.2.1" 232 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" 233 | integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== 234 | dependencies: 235 | is-callable "^1.1.4" 236 | is-date-object "^1.0.1" 237 | is-symbol "^1.0.2" 238 | 239 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: 240 | version "1.0.5" 241 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 242 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 243 | 244 | esprima@^4.0.0: 245 | version "4.0.1" 246 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 247 | integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== 248 | 249 | esutils@^2.0.2: 250 | version "2.0.3" 251 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" 252 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 253 | 254 | fclone@^1.0.11: 255 | version "1.0.11" 256 | resolved "https://registry.yarnpkg.com/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" 257 | integrity sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA= 258 | 259 | find-up@3.0.0, find-up@^3.0.0: 260 | version "3.0.0" 261 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" 262 | integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== 263 | dependencies: 264 | locate-path "^3.0.0" 265 | 266 | flat@^4.1.0: 267 | version "4.1.0" 268 | resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" 269 | integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== 270 | dependencies: 271 | is-buffer "~2.0.3" 272 | 273 | fs.realpath@^1.0.0: 274 | version "1.0.0" 275 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 276 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 277 | 278 | function-bind@^1.1.1: 279 | version "1.1.1" 280 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 281 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 282 | 283 | get-caller-file@^2.0.1: 284 | version "2.0.5" 285 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 286 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 287 | 288 | get-func-name@^2.0.0: 289 | version "2.0.0" 290 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 291 | integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= 292 | 293 | glob@7.1.3: 294 | version "7.1.3" 295 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 296 | integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== 297 | dependencies: 298 | fs.realpath "^1.0.0" 299 | inflight "^1.0.4" 300 | inherits "2" 301 | minimatch "^3.0.4" 302 | once "^1.3.0" 303 | path-is-absolute "^1.0.0" 304 | 305 | glob@^7.1.1: 306 | version "7.1.6" 307 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 308 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 309 | dependencies: 310 | fs.realpath "^1.0.0" 311 | inflight "^1.0.4" 312 | inherits "2" 313 | minimatch "^3.0.4" 314 | once "^1.3.0" 315 | path-is-absolute "^1.0.0" 316 | 317 | growl@1.10.5: 318 | version "1.10.5" 319 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" 320 | integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== 321 | 322 | has-flag@^3.0.0: 323 | version "3.0.0" 324 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 325 | integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= 326 | 327 | has-symbols@^1.0.0, has-symbols@^1.0.1: 328 | version "1.0.1" 329 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" 330 | integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== 331 | 332 | has@^1.0.3: 333 | version "1.0.3" 334 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 335 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 336 | dependencies: 337 | function-bind "^1.1.1" 338 | 339 | he@1.2.0: 340 | version "1.2.0" 341 | resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" 342 | integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== 343 | 344 | inflight@^1.0.4: 345 | version "1.0.6" 346 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 347 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 348 | dependencies: 349 | once "^1.3.0" 350 | wrappy "1" 351 | 352 | inherits@2: 353 | version "2.0.4" 354 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 355 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 356 | 357 | is-buffer@~2.0.3: 358 | version "2.0.4" 359 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" 360 | integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== 361 | 362 | is-callable@^1.1.4, is-callable@^1.1.5: 363 | version "1.1.5" 364 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" 365 | integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== 366 | 367 | is-date-object@^1.0.1: 368 | version "1.0.2" 369 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" 370 | integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== 371 | 372 | is-fullwidth-code-point@^2.0.0: 373 | version "2.0.0" 374 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 375 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 376 | 377 | is-regex@^1.0.5: 378 | version "1.0.5" 379 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" 380 | integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== 381 | dependencies: 382 | has "^1.0.3" 383 | 384 | is-symbol@^1.0.2: 385 | version "1.0.3" 386 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" 387 | integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== 388 | dependencies: 389 | has-symbols "^1.0.1" 390 | 391 | isexe@^2.0.0: 392 | version "2.0.0" 393 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 394 | integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= 395 | 396 | js-tokens@^4.0.0: 397 | version "4.0.0" 398 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 399 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 400 | 401 | js-yaml@3.13.1, js-yaml@^3.13.1: 402 | version "3.13.1" 403 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 404 | integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== 405 | dependencies: 406 | argparse "^1.0.7" 407 | esprima "^4.0.0" 408 | 409 | locate-path@^3.0.0: 410 | version "3.0.0" 411 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" 412 | integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== 413 | dependencies: 414 | p-locate "^3.0.0" 415 | path-exists "^3.0.0" 416 | 417 | lodash@^4.17.15: 418 | version "4.17.15" 419 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" 420 | integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== 421 | 422 | log-symbols@2.2.0: 423 | version "2.2.0" 424 | resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" 425 | integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== 426 | dependencies: 427 | chalk "^2.0.1" 428 | 429 | make-error@^1.1.1: 430 | version "1.3.5" 431 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" 432 | integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== 433 | 434 | minimatch@3.0.4, minimatch@^3.0.4: 435 | version "3.0.4" 436 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 437 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 438 | dependencies: 439 | brace-expansion "^1.1.7" 440 | 441 | minimist@0.0.8: 442 | version "0.0.8" 443 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 444 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 445 | 446 | mkdirp@0.5.1, mkdirp@^0.5.1: 447 | version "0.5.1" 448 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 449 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 450 | dependencies: 451 | minimist "0.0.8" 452 | 453 | mocha@^6.1.4: 454 | version "6.2.2" 455 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.2.tgz#5d8987e28940caf8957a7d7664b910dc5b2fea20" 456 | integrity sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A== 457 | dependencies: 458 | ansi-colors "3.2.3" 459 | browser-stdout "1.3.1" 460 | debug "3.2.6" 461 | diff "3.5.0" 462 | escape-string-regexp "1.0.5" 463 | find-up "3.0.0" 464 | glob "7.1.3" 465 | growl "1.10.5" 466 | he "1.2.0" 467 | js-yaml "3.13.1" 468 | log-symbols "2.2.0" 469 | minimatch "3.0.4" 470 | mkdirp "0.5.1" 471 | ms "2.1.1" 472 | node-environment-flags "1.0.5" 473 | object.assign "4.1.0" 474 | strip-json-comments "2.0.1" 475 | supports-color "6.0.0" 476 | which "1.3.1" 477 | wide-align "1.1.3" 478 | yargs "13.3.0" 479 | yargs-parser "13.1.1" 480 | yargs-unparser "1.6.0" 481 | 482 | ms@2.1.1: 483 | version "2.1.1" 484 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" 485 | integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== 486 | 487 | ms@^2.1.1: 488 | version "2.1.2" 489 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 490 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 491 | 492 | node-environment-flags@1.0.5: 493 | version "1.0.5" 494 | resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" 495 | integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== 496 | dependencies: 497 | object.getownpropertydescriptors "^2.0.3" 498 | semver "^5.7.0" 499 | 500 | object-inspect@^1.7.0: 501 | version "1.7.0" 502 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" 503 | integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== 504 | 505 | object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: 506 | version "1.1.1" 507 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 508 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 509 | 510 | object.assign@4.1.0, object.assign@^4.1.0: 511 | version "4.1.0" 512 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" 513 | integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== 514 | dependencies: 515 | define-properties "^1.1.2" 516 | function-bind "^1.1.1" 517 | has-symbols "^1.0.0" 518 | object-keys "^1.0.11" 519 | 520 | object.getownpropertydescriptors@^2.0.3: 521 | version "2.1.0" 522 | resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" 523 | integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== 524 | dependencies: 525 | define-properties "^1.1.3" 526 | es-abstract "^1.17.0-next.1" 527 | 528 | once@^1.3.0: 529 | version "1.4.0" 530 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 531 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 532 | dependencies: 533 | wrappy "1" 534 | 535 | p-limit@^2.0.0: 536 | version "2.2.2" 537 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" 538 | integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== 539 | dependencies: 540 | p-try "^2.0.0" 541 | 542 | p-locate@^3.0.0: 543 | version "3.0.0" 544 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" 545 | integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== 546 | dependencies: 547 | p-limit "^2.0.0" 548 | 549 | p-try@^2.0.0: 550 | version "2.2.0" 551 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 552 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 553 | 554 | path-exists@^3.0.0: 555 | version "3.0.0" 556 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 557 | integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= 558 | 559 | path-is-absolute@^1.0.0: 560 | version "1.0.1" 561 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 562 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 563 | 564 | path-parse@^1.0.6: 565 | version "1.0.6" 566 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" 567 | integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== 568 | 569 | pathval@^1.1.0: 570 | version "1.1.0" 571 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" 572 | integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= 573 | 574 | require-directory@^2.1.1: 575 | version "2.1.1" 576 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 577 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 578 | 579 | require-main-filename@^2.0.0: 580 | version "2.0.0" 581 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" 582 | integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== 583 | 584 | resolve@^1.3.2: 585 | version "1.15.0" 586 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" 587 | integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== 588 | dependencies: 589 | path-parse "^1.0.6" 590 | 591 | semver@^5.3.0, semver@^5.7.0: 592 | version "5.7.1" 593 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 594 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 595 | 596 | set-blocking@^2.0.0: 597 | version "2.0.0" 598 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 599 | integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= 600 | 601 | source-map-support@^0.5.6: 602 | version "0.5.16" 603 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" 604 | integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== 605 | dependencies: 606 | buffer-from "^1.0.0" 607 | source-map "^0.6.0" 608 | 609 | source-map@^0.6.0: 610 | version "0.6.1" 611 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 612 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 613 | 614 | sprintf-js@~1.0.2: 615 | version "1.0.3" 616 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 617 | integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= 618 | 619 | "string-width@^1.0.2 || 2": 620 | version "2.1.1" 621 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 622 | integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 623 | dependencies: 624 | is-fullwidth-code-point "^2.0.0" 625 | strip-ansi "^4.0.0" 626 | 627 | string-width@^3.0.0, string-width@^3.1.0: 628 | version "3.1.0" 629 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" 630 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== 631 | dependencies: 632 | emoji-regex "^7.0.1" 633 | is-fullwidth-code-point "^2.0.0" 634 | strip-ansi "^5.1.0" 635 | 636 | string.prototype.trimleft@^2.1.1: 637 | version "2.1.1" 638 | resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" 639 | integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== 640 | dependencies: 641 | define-properties "^1.1.3" 642 | function-bind "^1.1.1" 643 | 644 | string.prototype.trimright@^2.1.1: 645 | version "2.1.1" 646 | resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" 647 | integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== 648 | dependencies: 649 | define-properties "^1.1.3" 650 | function-bind "^1.1.1" 651 | 652 | strip-ansi@^4.0.0: 653 | version "4.0.0" 654 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 655 | integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 656 | dependencies: 657 | ansi-regex "^3.0.0" 658 | 659 | strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: 660 | version "5.2.0" 661 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" 662 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== 663 | dependencies: 664 | ansi-regex "^4.1.0" 665 | 666 | strip-json-comments@2.0.1: 667 | version "2.0.1" 668 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 669 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 670 | 671 | supports-color@6.0.0: 672 | version "6.0.0" 673 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" 674 | integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== 675 | dependencies: 676 | has-flag "^3.0.0" 677 | 678 | supports-color@^5.3.0: 679 | version "5.5.0" 680 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" 681 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== 682 | dependencies: 683 | has-flag "^3.0.0" 684 | 685 | ts-node@^8.3.0: 686 | version "8.6.2" 687 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" 688 | integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== 689 | dependencies: 690 | arg "^4.1.0" 691 | diff "^4.0.1" 692 | make-error "^1.1.1" 693 | source-map-support "^0.5.6" 694 | yn "3.1.1" 695 | 696 | tslib@^1.10.0, tslib@^1.8.0, tslib@^1.8.1: 697 | version "1.10.0" 698 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" 699 | integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== 700 | 701 | tslint@^5.18.0: 702 | version "5.20.1" 703 | resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" 704 | integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== 705 | dependencies: 706 | "@babel/code-frame" "^7.0.0" 707 | builtin-modules "^1.1.1" 708 | chalk "^2.3.0" 709 | commander "^2.12.1" 710 | diff "^4.0.1" 711 | glob "^7.1.1" 712 | js-yaml "^3.13.1" 713 | minimatch "^3.0.4" 714 | mkdirp "^0.5.1" 715 | resolve "^1.3.2" 716 | semver "^5.3.0" 717 | tslib "^1.8.0" 718 | tsutils "^2.29.0" 719 | 720 | tsutils@^2.29.0: 721 | version "2.29.0" 722 | resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" 723 | integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== 724 | dependencies: 725 | tslib "^1.8.1" 726 | 727 | type-detect@^4.0.0, type-detect@^4.0.5: 728 | version "4.0.8" 729 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 730 | integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== 731 | 732 | typescript@^3.5.3: 733 | version "3.7.5" 734 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" 735 | integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== 736 | 737 | which-module@^2.0.0: 738 | version "2.0.0" 739 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 740 | integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= 741 | 742 | which@1.3.1: 743 | version "1.3.1" 744 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 745 | integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== 746 | dependencies: 747 | isexe "^2.0.0" 748 | 749 | wide-align@1.1.3: 750 | version "1.1.3" 751 | resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" 752 | integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== 753 | dependencies: 754 | string-width "^1.0.2 || 2" 755 | 756 | wrap-ansi@^5.1.0: 757 | version "5.1.0" 758 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" 759 | integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== 760 | dependencies: 761 | ansi-styles "^3.2.0" 762 | string-width "^3.0.0" 763 | strip-ansi "^5.0.0" 764 | 765 | wrappy@1: 766 | version "1.0.2" 767 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 768 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 769 | 770 | y18n@^4.0.0: 771 | version "4.0.0" 772 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" 773 | integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== 774 | 775 | yargs-parser@13.1.1, yargs-parser@^13.1.1: 776 | version "13.1.1" 777 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" 778 | integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== 779 | dependencies: 780 | camelcase "^5.0.0" 781 | decamelize "^1.2.0" 782 | 783 | yargs-unparser@1.6.0: 784 | version "1.6.0" 785 | resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" 786 | integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== 787 | dependencies: 788 | flat "^4.1.0" 789 | lodash "^4.17.15" 790 | yargs "^13.3.0" 791 | 792 | yargs@13.3.0, yargs@^13.3.0: 793 | version "13.3.0" 794 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" 795 | integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== 796 | dependencies: 797 | cliui "^5.0.0" 798 | find-up "^3.0.0" 799 | get-caller-file "^2.0.1" 800 | require-directory "^2.1.1" 801 | require-main-filename "^2.0.0" 802 | set-blocking "^2.0.0" 803 | string-width "^3.0.0" 804 | which-module "^2.0.0" 805 | y18n "^4.0.0" 806 | yargs-parser "^13.1.1" 807 | 808 | yn@3.1.1: 809 | version "3.1.1" 810 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 811 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 812 | --------------------------------------------------------------------------------