diff --git a/.gitignore b/.gitignore index 14b6473b72..f040307c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,6 @@ dist/ .vscode/settings.json .vimrc .nvimrc +syntaxes/out syntaxes/test/*.js syntaxes/test/*.map diff --git a/package.json b/package.json index c34119b950..6667fec630 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,8 @@ "compile:test": "tsc -b server/src/tests", "compile:integration": "tsc -b integration", "compile:syntaxes-test": "tsc -b syntaxes/test", + "compile:syntaxes": "tsc -b syntaxes", + "build:syntaxes": "yarn compile:syntaxes && node syntaxes/out/build.js", "format": "scripts/format.sh", "watch": "tsc -b -w", "postinstall": "vscode-install && cd client && yarn && cd ../server && yarn && cd ..", @@ -103,7 +105,7 @@ "test": "yarn compile:test && jasmine server/out/tests/*_spec.js", "test:lsp": "yarn compile:integration && jasmine integration/out/lsp/*_spec.js", "test:e2e": "yarn compile:integration && ./scripts/e2e.sh", - "test:syntaxes": "yarn compile:syntaxes-test && jasmine syntaxes/test/driver.js" + "test:syntaxes": "yarn compile:syntaxes-test && yarn build:syntaxes && jasmine syntaxes/test/driver.js" }, "dependencies": { "typescript": "~3.7.4" diff --git a/scripts/build.sh b/scripts/build.sh index 41bd943594..cfc16526a5 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -5,6 +5,7 @@ set -ex -o pipefail # Clean up from last build rm -rf client/out rm -rf server/out +rm -rf syntaxes/out rm -rf dist rm -rf **/*.tsbuildinfo @@ -17,9 +18,13 @@ cp package.json yarn.lock angular.png CHANGELOG.md README.md dist cp client/package.json client/yarn.lock dist/client # Copy files to server directory cp server/package.json server/yarn.lock server/README.md dist/server -# Copy files to syntaxes directory +# Build and copy files to syntaxes directory +node syntaxes/out/build.js mkdir dist/syntaxes +cp syntaxes/out/*.json dist/syntaxes +# TODO: Remove the next two lines once all syntaxes have been refactored to TypeScript cp syntaxes/*.json dist/syntaxes +rm dist/syntaxes/tsconfig.json pushd dist yarn install --production --ignore-scripts diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh index bc632af089..22ab2e4c77 100755 --- a/scripts/cleanup.sh +++ b/scripts/cleanup.sh @@ -5,3 +5,4 @@ set -ex -o pipefail # Clean up from last build rm -rf client/out rm -rf server/out +rm -rf syntaxes/out diff --git a/syntaxes/src/build.ts b/syntaxes/src/build.ts new file mode 100644 index 0000000000..296fa75af7 --- /dev/null +++ b/syntaxes/src/build.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import * as fs from 'fs'; + +import {GrammarDefinition, JsonObject} from './types'; +import {template} from './template/grammar'; + +// Recursively transforms a TypeScript grammar definition into an object which can be processed by +// JSON.stringify to generate a valid TextMate JSON grammar definition +function processGrammar(grammar: GrammarDefinition): JsonObject { + const processedGrammar: JsonObject = {}; + for (const [key, value] of Object.entries(grammar)) { + if (typeof value === 'string') { + processedGrammar[key] = value; + } else if (value instanceof RegExp) { + // Escape backslashes/quote marks and trim the demarcating `/` characters from a regex literal + processedGrammar[key] = value.toString().replace(/^\/|\/$/g, ''); + } else if (value instanceof Array) { + processedGrammar[key] = value.map(processGrammar); + } else { + processedGrammar[key] = processGrammar(value); + } + } + return processedGrammar; +} + +// Build a TextMate grammar JSON file from a source TypeScript object +function build(grammar: GrammarDefinition, filename: string): void { + const processedGrammar: JsonObject = processGrammar(grammar); + const grammarContent: string = JSON.stringify(processedGrammar, null, ' ') + '\n'; + + fs.writeFile(`syntaxes/out/${filename}.json`, grammarContent, (error) => { + if (error) throw error; + }); +} + +build(template, 'template'); diff --git a/syntaxes/src/template/grammar.ts b/syntaxes/src/template/grammar.ts new file mode 100644 index 0000000000..34af6e4f12 --- /dev/null +++ b/syntaxes/src/template/grammar.ts @@ -0,0 +1,150 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {GrammarDefinition} from '../types'; + +export const template: GrammarDefinition = { + scopeName: 'template.ng', + injectionSelector: 'L:text.html -comment', + patterns: [ + {include: '#interpolation'}, + {include: '#propertyBinding'}, + {include: '#eventBinding'}, + {include: '#twoWayBinding'}, + {include: '#templateBinding'}, + ], + repository: { + + interpolation: { + begin: /{{/, + beginCaptures: { + 0: {name: 'punctuation.definition.block.ts'}, + }, + end: /}}/, + endCaptures: { + 0: {name: 'punctuation.definition.block.ts'}, + }, + contentName: 'source.js', + patterns: [ + {include: 'source.js'}, + ], + }, + + propertyBinding: { + begin: /(\[\s*@?[-_a-zA-Z0-9.$]*\s*])(=)(["'])/, + beginCaptures: { + 1: { + name: 'entity.other.attribute-name.html entity.other.ng-binding-name.property.html', + patterns: [ + {include: '#bindingKey'}, + ], + }, + 2: {name: 'punctuation.separator.key-value.html'}, + 3: {name: 'string.quoted.html punctuation.definition.string.begin.html'}, + }, + end: /\3/, + endCaptures: { + 0: {name: 'string.quoted.html punctuation.definition.string.end.html'}, + }, + name: 'meta.ng-binding.property.html', + contentName: 'source.js', + patterns: [ + {include: 'source.js'}, + ], + }, + + eventBinding: { + begin: /(\(\s*@?[-_a-zA-Z0-9.$]*\s*\))(=)(["'])/, + beginCaptures: { + 1: { + name: 'entity.other.attribute-name.html entity.other.ng-binding-name.event.html', + patterns: [ + {include: '#bindingKey'}, + ], + }, + 2: {name: 'punctuation.separator.key-value.html'}, + 3: {name: 'string.quoted.html punctuation.definition.string.begin.html'}, + }, + end: /\3/, + endCaptures: { + 0: {name: 'string.quoted.html punctuation.definition.string.end.html'}, + }, + name: 'meta.ng-binding.event.html', + contentName: 'source.js', + patterns: [ + {include: 'source.js'}, + ], + }, + + twoWayBinding: { + begin: /(\[\s*\(\s*@?[-_a-zA-Z0-9.$]*\s*\)\s*\])(=)(["'])/, + beginCaptures: { + 1: { + name: 'entity.other.attribute-name.html entity.other.ng-binding-name.two-way.html', + patterns: [ + {include: '#bindingKey'}, + ], + }, + 2: {name: 'punctuation.separator.key-value.html'}, + 3: {name: 'string.quoted.html punctuation.definition.string.begin.html'}, + }, + end: /\3/, + endCaptures: { + 0: {name: 'string.quoted.html punctuation.definition.string.end.html'}, + }, + name: 'meta.ng-binding.two-way.html', + contentName: 'source.js', + patterns: [ + {include: 'source.js'}, + ], + }, + + templateBinding: { + begin: /(\*[-_a-zA-Z0-9.$]*)(=)(["'])/, + beginCaptures: { + 1: { + name: 'entity.other.attribute-name.html entity.other.ng-binding-name.template.html', + patterns: [ + {include: '#bindingKey'}, + ], + }, + 2: {name: 'punctuation.separator.key-value.html'}, + 3: {name: 'string.quoted.html punctuation.definition.string.begin.html'}, + }, + end: /\3/, + endCaptures: { + 0: {name: 'string.quoted.html punctuation.definition.string.end.html'}, + }, + name: 'meta.ng-binding.template.html', + contentName: 'source.js', + patterns: [ + {include: 'source.js'}, + ], + }, + + bindingKey: { + patterns: [ + { + match: /([\[\(]{1,2})(?:\s*)(@?[-_a-zA-Z0-9.$]*)(?:\s*)([\]\)]{1,2})/, + captures: { + 1: {name: 'punctuation.definition.ng-binding-name.begin.html'}, + 2: { + patterns: [ + { + match: /\./, + name: 'punctuation.accessor.html', + }, + ], + }, + 3: {name: 'punctuation.definition.ng-binding-name.end.html'}, + }, + }, + ], + }, + }, +}; diff --git a/syntaxes/src/types.ts b/syntaxes/src/types.ts new file mode 100644 index 0000000000..6dea0ea687 --- /dev/null +++ b/syntaxes/src/types.ts @@ -0,0 +1,15 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export interface GrammarDefinition { + [key: string]: string|RegExp|GrammarDefinition|GrammarDefinition[]; +} + +export interface JsonObject { + [key: string]: string|JsonObject|JsonObject[]; +} diff --git a/syntaxes/template.json b/syntaxes/template.json deleted file mode 100644 index e0a84e8f9b..0000000000 --- a/syntaxes/template.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "scopeName": "template.ng", - "injectionSelector": "L:text.html -comment", - "patterns": [ - { - "include": "#interpolation" - }, - { - "include": "#propertyBinding" - }, - { - "include": "#eventBinding" - }, - { - "include": "#twoWayBinding" - }, - { - "include": "#templateBinding" - } - ], - "repository": { - "interpolation": { - "begin": "{{", - "beginCaptures": { - "0": { - "name": "punctuation.definition.block.ts" - } - }, - "end": "}}", - "endCaptures": { - "0": { - "name": "punctuation.definition.block.ts" - } - }, - "contentName": "source.js", - "patterns": [ - { - "include": "source.js" - } - ] - }, - - "propertyBinding": { - "begin": "(\\[\\s*@?[-_a-zA-Z0-9.$]*\\s*])(=)([\"'])", - "beginCaptures": { - "1": { - "name": "entity.other.attribute-name.html entity.other.ng-binding-name.property.html", - "patterns": [ - { - "include": "#bindingKey" - } - ] - }, - "2": { - "name": "punctuation.separator.key-value.html" - }, - "3": { - "name": "string.quoted.html punctuation.definition.string.begin.html" - } - }, - "end": "\\3", - "endCaptures": { - "0": { - "name": "string.quoted.html punctuation.definition.string.end.html" - } - }, - "name": "meta.ng-binding.property.html", - "contentName": "source.js", - "patterns": [ - { - "include": "source.js" - } - ] - }, - - "eventBinding": { - "begin": "(\\(\\s*@?[-_a-zA-Z0-9.$]*\\s*\\))(=)([\"'])", - "beginCaptures": { - "1": { - "name": "entity.other.attribute-name.html entity.other.ng-binding-name.event.html", - "patterns": [ - { - "include": "#bindingKey" - } - ] - }, - "2": { - "name": "punctuation.separator.key-value.html" - }, - "3": { - "name": "string.quoted.html punctuation.definition.string.begin.html" - } - }, - "end": "\\3", - "endCaptures": { - "0": { - "name": "string.quoted.html punctuation.definition.string.end.html" - } - }, - "name": "meta.ng-binding.event.html", - "contentName": "source.js", - "patterns": [ - { - "include": "source.js" - } - ] - }, - - "twoWayBinding": { - "begin": "(\\[\\s*\\(\\s*@?[-_a-zA-Z0-9.$]*\\s*\\)\\s*\\])(=)([\"'])", - "beginCaptures": { - "1": { - "name": "entity.other.attribute-name.html entity.other.ng-binding-name.two-way.html", - "patterns": [ - { - "include": "#bindingKey" - } - ] - }, - "2": { - "name": "punctuation.separator.key-value.html" - }, - "3": { - "name": "string.quoted.html punctuation.definition.string.begin.html" - } - }, - "end": "\\3", - "endCaptures": { - "0": { - "name": "string.quoted.html punctuation.definition.string.end.html" - } - }, - "name": "meta.ng-binding.two-way.html", - "contentName": "source.js", - "patterns": [ - { - "include": "source.js" - } - ] - }, - - "templateBinding": { - "begin": "(\\*[-_a-zA-Z0-9.$]*)(=)([\"'])", - "beginCaptures": { - "1": { - "name": "entity.other.attribute-name.html entity.other.ng-binding-name.template.html", - "patterns": [ - { - "include": "#bindingKey" - } - ] - }, - "2": { - "name": "punctuation.separator.key-value.html" - }, - "3": { - "name": "string.quoted.html punctuation.definition.string.begin.html" - } - }, - "end": "\\3", - "endCaptures": { - "0": { - "name": "string.quoted.html punctuation.definition.string.end.html" - } - }, - "name": "meta.ng-binding.template.html", - "contentName": "source.js", - "patterns": [ - { - "include": "source.js" - } - ] - }, - - "bindingKey": { - "patterns": [ - { - "match": "([\\[\\(]{1,2})(?:\\s*)(@?[-_a-zA-Z0-9.$]*)(?:\\s*)([\\]\\)]{1,2})", - "captures": { - "1": { - "name": "punctuation.definition.ng-binding-name.begin.html" - }, - "2": { - "patterns": [ - { - "match": "\\.", - "name": "punctuation.accessor.html" - } - ] - }, - "3": { - "name": "punctuation.definition.ng-binding-name.end.html" - } - } - } - ] - } - } -} diff --git a/syntaxes/test/cases.json b/syntaxes/test/cases.json index 25ef81fddc..b4b6a96b9b 100644 --- a/syntaxes/test/cases.json +++ b/syntaxes/test/cases.json @@ -2,7 +2,10 @@ { "name": "inline template", "scopeName": "inline-template.ng", - "grammarFiles": ["syntaxes/inline-template.json", "syntaxes/template.json"], + "grammarFiles": [ + "syntaxes/inline-template.json", + "syntaxes/out/template.json" + ], "testFile": "syntaxes/test/data/inline-template.ts" }, { @@ -14,7 +17,7 @@ { "name": "template syntax", "scopeName": "template.ng", - "grammarFiles": ["syntaxes/template.json"], + "grammarFiles": ["syntaxes/out/template.json"], "testFile": "syntaxes/test/data/template.html" } ] diff --git a/syntaxes/tsconfig.json b/syntaxes/tsconfig.json new file mode 100644 index 0000000000..046fa6b876 --- /dev/null +++ b/syntaxes/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "out" + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json index bf09880226..7e5240c4cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "files": [], "references": [ { "path": "./client" }, - { "path": "./server" } + { "path": "./server" }, + { "path": "./syntaxes" } ] }