angular-server-side-configuration
Advanced tools
Comparing version
@@ -1,2 +0,10 @@ | ||
declare const _default: import("@angular-devkit/architect/src/internal").Builder<import("../../models").Options>; | ||
export default _default; | ||
import { JsonObject } from '@angular-devkit/core'; | ||
interface Schema { | ||
additionalEnvironmentVariables: string[]; | ||
aotSupport: boolean; | ||
browserTarget: string; | ||
ngsscEnvironmentFile: string; | ||
filePattern: string | null; | ||
} | ||
declare const _default: import("@angular-devkit/architect/src/internal").Builder<Schema & JsonObject>; | ||
export { _default as default, Schema as NgsscBuildSchema }; |
@@ -1,5 +0,227 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const architect_1 = require("@angular-devkit/architect"); | ||
const ngssc_builder_1 = require("./ngssc-builder"); | ||
exports.default = architect_1.createBuilder(ngssc_builder_1.NgsscBuilder.build); | ||
'use strict'; | ||
var architect = require('@angular-devkit/architect'); | ||
var crypto = require('crypto'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var util = require('util'); | ||
var typescript = require('typescript'); | ||
/** | ||
* Detect environment variables in given file. | ||
* @public | ||
*/ | ||
class VariableDetector { | ||
detect(fileContent) { | ||
const fileMetaData = typescript.createSourceFile('environment.ts', fileContent, typescript.ScriptTarget.ESNext, true); | ||
const { variant, variantImport } = this._detectVariant(fileMetaData); | ||
const variables = variant === 'process' | ||
? this._findProcessEnvVariables(fileMetaData) | ||
: this._findNgEnvVariables(fileMetaData); | ||
return { | ||
variables: variables.sort((a, b) => b.variable.length - a.variable.length), | ||
variant, | ||
variantImport, | ||
}; | ||
} | ||
_detectVariant(node) { | ||
const variantImport = this._findNodesOfType(node, typescript.SyntaxKind.ImportDeclaration) | ||
.map(n => n.getFullText().trim()) | ||
.filter(n => n.includes('angular-server-side-configuration'))[0]; | ||
if (!variantImport || variantImport.match(/angular-server-side-configuration\/process/)) { | ||
return { variant: 'process', variantImport }; | ||
} | ||
const variant = variantImport.match(/angular-server-side-configuration\/ng-env/) ? 'NG_ENV' : undefined; | ||
if (!variant) { | ||
throw new Error('Could not detect variant (expected either process or ng-env)'); | ||
} | ||
return { variant, variantImport }; | ||
} | ||
_findProcessEnvVariables(node) { | ||
return this._findUsages(node, 'process') | ||
.sort((a, b) => b.parent.parent.getText().length - a.parent.parent.getText().length) | ||
.map(n => ({ | ||
expression: this._resolveExpression(n.parent), | ||
variable: n.parent.parent.getText().split('.')[2], | ||
})); | ||
} | ||
_findNgEnvVariables(node) { | ||
return this._findUsages(node, 'NG_ENV') | ||
.filter(n => n.kind === typescript.SyntaxKind.Identifier && n.parent.kind !== typescript.SyntaxKind.ImportSpecifier) | ||
.sort((a, b) => b.parent.getText().length - a.parent.getText().length) | ||
.map(n => ({ | ||
expression: this._resolveExpression(n), | ||
variable: n.parent.getText().split('.')[1], | ||
})); | ||
} | ||
_findNodesOfType(node, kind) { | ||
return node | ||
.getChildren() | ||
.map(c => this._findNodesOfType(c, kind)) | ||
.reduce((current, next) => current.concat(...next), node.kind === kind ? [node] : []); | ||
} | ||
_findUsages(node, variant) { | ||
return node | ||
.getChildren() | ||
.map(c => this._findUsages(c, variant)) | ||
.reduce((current, next) => current.concat(next), node.getText() === variant ? [node] : []); | ||
} | ||
_resolveExpression(node) { | ||
while (true) { | ||
if (!typescript.SyntaxKind[node.parent.kind].endsWith('Expression')) { | ||
return node.getText(); | ||
} | ||
node = node.parent; | ||
} | ||
} | ||
} | ||
/** | ||
* The result class for VariableTokenizer. | ||
* @public | ||
*/ | ||
class TokenizeResult { | ||
constructor(content, _variables) { | ||
this.content = content; | ||
this._variables = _variables; | ||
} | ||
untokenize(fileContent) { | ||
if (!this._variables.some(v => fileContent.includes(v.token))) { | ||
return fileContent; | ||
} | ||
return this._variables.reduce((current, next) => current | ||
.replace(new RegExp(`"${next.token}"`, 'g'), next.expression) | ||
.replace(new RegExp(`"${next.token}`, 'g'), `(${next.expression}) + "`) | ||
.replace(new RegExp(`${next.token}"`, 'g'), `" + (${next.expression})`) | ||
.replace(new RegExp(`${next.token}`, 'g'), `" + (${next.expression}) + "`), fileContent); | ||
} | ||
} | ||
/** | ||
* Tokenize variables in a given file. | ||
* @public | ||
*/ | ||
class VariableTokenizer { | ||
constructor() { | ||
this._tokenCounter = 0; | ||
} | ||
tokenize(sourceContent, ngsscContext) { | ||
const tokenizedVariables = ngsscContext.variables | ||
.map(v => ({ ...v, token: `ngssc-token-${++this._tokenCounter}-${Date.now()}` })); | ||
const tokenizedFileContent = tokenizedVariables | ||
.reduce((current, next) => current.replace(next.expression, `"${next.token}" as any`), this._adaptImport(sourceContent, ngsscContext)); | ||
return new TokenizeResult(tokenizedFileContent, tokenizedVariables); | ||
} | ||
_adaptImport(fileContent, ngsscContext) { | ||
return ngsscContext.variant === 'process' || !ngsscContext.variantImport | ||
? fileContent | ||
: fileContent.replace(ngsscContext.variantImport, `import 'angular-server-side-configuration/ng-env';`); | ||
} | ||
} | ||
const readFileAsync = util.promisify(fs.readFile); | ||
const writeFileAsync = util.promisify(fs.writeFile); | ||
const unlinkAsync = util.promisify(fs.unlink); | ||
const readdirAsync = util.promisify(fs.readdir); | ||
class NgsscBuilder { | ||
constructor(_options, _context) { | ||
this._options = _options; | ||
this._context = _context; | ||
this._detector = new VariableDetector(); | ||
this._tokenizer = new VariableTokenizer(); | ||
this._browserTarget = architect.targetFromTargetString(_options.browserTarget); | ||
this._ngsscEnvironmentFile = path.join(_context.workspaceRoot, _options.ngsscEnvironmentFile); | ||
this._tmpNgsscEnvironmentFile = `${_options.ngsscEnvironmentFile}_${crypto.randomBytes(10).toString('hex')}.tmp`; | ||
this._context.addTeardown(() => this._removeTmpNgsscEnvironmentFile()); | ||
} | ||
async run() { | ||
try { | ||
return await this._safeRun(); | ||
} | ||
catch (e) { | ||
this._context.logger.error(e.toString()); | ||
return process.exit(2); | ||
} | ||
} | ||
async _safeRun() { | ||
const ngsscContext = await this._detectVariables(); | ||
const rawOptions = await this._prepareBrowserOptions(ngsscContext); | ||
const browserTarget = this._browserTarget; | ||
const browserName = await this._context.getBuilderNameForTarget(browserTarget); | ||
const browserOptions = await this._context.validateOptions(rawOptions, browserName); | ||
const scheduledTarget = await this._context.scheduleTarget(browserTarget, browserOptions); | ||
const result = await scheduledTarget.result; | ||
await this._buildNgsscJson(ngsscContext, browserOptions); | ||
await this._untokenize(browserOptions); | ||
await this._removeTmpNgsscEnvironmentFile(); | ||
return result; | ||
} | ||
async _detectVariables() { | ||
const fileContent = await readFileAsync(this._ngsscEnvironmentFile, 'utf8'); | ||
const ngsscContext = await this._detector.detect(fileContent); | ||
this._context.logger.info(`ngssc: Detected variant '${ngsscContext.variant}' with variables ` + | ||
`'${ngsscContext.variables.map(v => v.variable).join(', ')}'`); | ||
return ngsscContext; | ||
} | ||
async _prepareBrowserOptions(ngsscContext) { | ||
const rawBrowserOptions = await this._context.getTargetOptions(this._browserTarget); | ||
if (!this._options.aotSupport) { | ||
return rawBrowserOptions; | ||
} | ||
const tmpNgsscEnvironmentFilePath = path.join(this._context.workspaceRoot, this._tmpNgsscEnvironmentFile); | ||
const ngsscEnvironmentFileContent = await readFileAsync(this._ngsscEnvironmentFile, 'utf8'); | ||
this._tokenizeResult = this._tokenizer.tokenize(ngsscEnvironmentFileContent, ngsscContext); | ||
await writeFileAsync(tmpNgsscEnvironmentFilePath, this._tokenizeResult.content, 'utf8'); | ||
return { ...rawBrowserOptions, fileReplacements: this._buildFileReplacements(rawBrowserOptions) }; | ||
} | ||
_buildFileReplacements(rawBrowserOptions) { | ||
const fileReplacements = (rawBrowserOptions.fileReplacements || []) | ||
.map(f => 'with' in f ? { ...f } : { replace: f.src, with: f.replaceWith }) | ||
.filter(f => f.with === this._options.ngsscEnvironmentFile) | ||
.map(f => ({ replace: f.replace, with: this._tmpNgsscEnvironmentFile })); | ||
if (!fileReplacements.length) { | ||
throw new Error(`Expected a fileReplacements entry in the referenced browserTarget '${this._options.browserTarget}'` + | ||
`, which uses ${this._options.ngsscEnvironmentFile} as a replacement! (e.g. "fileReplacements": ` + | ||
`[{ "replace": "src/environments/environment.ts", "with": "${this._options.ngsscEnvironmentFile}" }])`); | ||
} | ||
return fileReplacements; | ||
} | ||
async _buildNgsscJson(ngsscContext, browserOptions) { | ||
const outputPath = path.join(this._context.workspaceRoot, browserOptions.outputPath); | ||
const ngssc = { | ||
environmentVariables: [ | ||
...ngsscContext.variables.map(m => m.variable), | ||
...(this._options.additionalEnvironmentVariables || []), | ||
], | ||
filePattern: this._options.filePattern || path.basename(browserOptions.index), | ||
variant: ngsscContext.variant, | ||
}; | ||
await writeFileAsync(path.join(outputPath, 'ngssc.json'), JSON.stringify(ngssc, null, 2), 'utf8'); | ||
} | ||
async _untokenize(browserOptions) { | ||
if (!this._tokenizeResult) { | ||
return; | ||
} | ||
const outputPath = path.join(this._context.workspaceRoot, browserOptions.outputPath); | ||
const files = await readdirAsync(outputPath); | ||
for (const file of files | ||
.filter(f => f.endsWith('.js')) | ||
.map(f => path.join(outputPath, f))) { | ||
const fileContent = await readFileAsync(file, 'utf8'); | ||
const newFileContent = this._tokenizeResult.untokenize(fileContent); | ||
if (fileContent !== newFileContent) { | ||
await writeFileAsync(file, newFileContent, 'utf8'); | ||
} | ||
} | ||
} | ||
async _removeTmpNgsscEnvironmentFile() { | ||
const tmpNgsscEnvironmentFilePath = path.join(this._context.workspaceRoot, this._tmpNgsscEnvironmentFile); | ||
if (fs.existsSync(tmpNgsscEnvironmentFilePath)) { | ||
await unlinkAsync(tmpNgsscEnvironmentFilePath); | ||
} | ||
} | ||
} | ||
var index = architect.createBuilder(async (options, context) => await new NgsscBuilder(options, context).run()); | ||
module.exports = index; |
@@ -11,3 +11,5 @@ { | ||
"type": "boolean", | ||
"description": "Whether variables are going to be used in AoT contexts (like forRoot(...) or forChild(...))" | ||
"description": "Whether variables are going to be used in AoT contexts (like forRoot(...) or forChild(...))", | ||
"x-deprecated": true, | ||
"default": false | ||
}, | ||
@@ -14,0 +16,0 @@ "browserTarget": { |
@@ -5,2 +5,10 @@ # Changelog | ||
## [9.0.0-next.0](https://github.com/kyubisation/angular-server-side-configuration/compare/v8.2.1...v9.0.0-next.0) (2020-02-01) | ||
### Features | ||
* make insert commands idempotent ([3c55e34](https://github.com/kyubisation/angular-server-side-configuration/commit/3c55e34eb210033b976c4a8208023d8ef98580f6)) | ||
* upgrade to angular 9-rc ([f948ae5](https://github.com/kyubisation/angular-server-side-configuration/commit/f948ae5d5e5085bda112ffa77f9ee5f43713628d)) | ||
### [8.2.1](https://github.com/kyubisation/angular-server-side-configuration/compare/v8.2.0...v8.2.1) (2020-01-23) | ||
@@ -7,0 +15,0 @@ |
@@ -46,13 +46,4 @@ { | ||
"pattern": "^(.*)$" | ||
}, | ||
"insertInHead": { | ||
"$id": "#/properties/insertInHead", | ||
"type": "boolean", | ||
"title": "The Insertinhead Schema", | ||
"default": false, | ||
"examples": [ | ||
false | ||
] | ||
} | ||
} | ||
} |
{ | ||
"name": "angular-server-side-configuration", | ||
"version": "8.2.1", | ||
"version": "9.0.0-next.0", | ||
"description": "Configure an angular application on the server", | ||
"publishConfig": { | ||
"tag": "next" | ||
}, | ||
"builders": "./builders/builders.json", | ||
@@ -12,3 +15,3 @@ "schematics": "./schematics/collection.json", | ||
"clean": "rimraf coverage dist {builders,models,schematics,src}/**/*.{d.ts,js} test/*.{d.ts,js} junit.xml", | ||
"build:node": "npm run clean && tsc && rollup -c rollup.config.js", | ||
"build:node": "npm run clean && rollup -c rollup.config.js", | ||
"build:go": "docker-compose run build-go", | ||
@@ -25,3 +28,3 @@ "build": "run-p build:*", | ||
"module": "./src/module.js", | ||
"typings": "./src/index.d.ts", | ||
"typings": "./src/module.d.ts", | ||
"files": [ | ||
@@ -53,24 +56,24 @@ "**/*.{js,d.ts,json}", | ||
"devDependencies": { | ||
"@angular-devkit/architect": "^0.800.0", | ||
"@angular-devkit/core": "^8.0.0", | ||
"@angular-devkit/schematics": "^8.0.0", | ||
"@schematics/angular": "^8.0.0", | ||
"@angular-devkit/architect": "~0.900.0-rc.11", | ||
"@angular-devkit/core": "^9.0.0-rc.11", | ||
"@angular-devkit/schematics": "^9.0.0-rc.11", | ||
"@schematics/angular": "^9.0.0-rc.11", | ||
"@types/glob-to-regexp": "^0.4.0", | ||
"@types/jest": "^24.0.15", | ||
"@types/node": "^10.14.4", | ||
"@types/rimraf": "^2.0.2", | ||
"@types/jest": "^24.9.1", | ||
"@types/node": "^10.17.14", | ||
"@types/rimraf": "^2.0.3", | ||
"@wessberg/rollup-plugin-ts": "^1.2.15", | ||
"glob-to-regexp": "^0.4.1", | ||
"jest": "^24.8.0", | ||
"jest": "^24.9.0", | ||
"jest-junit": "^6.4.0", | ||
"npm-run-all": "^4.1.5", | ||
"rimraf": "^2.6.3", | ||
"rollup": "^1.17.0", | ||
"rollup-plugin-commonjs": "^10.0.1", | ||
"rimraf": "^2.7.1", | ||
"rollup": "^1.30.1", | ||
"rollup-plugin-commonjs": "^10.1.0", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"rollup-plugin-typescript2": "^0.22.0", | ||
"standard-version": "^7.0.1", | ||
"ts-jest": "^24.0.2", | ||
"ts-node": "^8.3.0", | ||
"tslint": "^5.18.0", | ||
"typescript": "~3.4.3" | ||
"standard-version": "^7.1.0", | ||
"ts-jest": "^24.3.0", | ||
"ts-node": "^8.6.2", | ||
"tslint": "^5.20.1", | ||
"typescript": "~3.6.4" | ||
}, | ||
@@ -77,0 +80,0 @@ "jest": { |
@@ -24,3 +24,3 @@ # angular-server-side-configuration | ||
## Version 8 Rewrite | ||
Version 8.x of this package is a complete rewrite with Angular schematics and builders. | ||
Version 8.x of this package was a complete rewrite with Angular schematics and builders. | ||
If you require support for older Angular versions, | ||
@@ -30,2 +30,7 @@ [Version 2.x](https://www.npmjs.com/package/angular-server-side-configuration/v/2.0.0) | ||
## Version 9 Change | ||
Version 9 of angular-server-side-configuration deprecates aotSupport, since it is | ||
no longer required for Angular 9 with Ivy. The update schematic removes the option | ||
from your angular.json. | ||
## Getting Started | ||
@@ -68,7 +73,7 @@ ``` | ||
"additionalEnvironmentVariables": ["MANUAL_ENTRIES"], | ||
"aotSupport": true, // Set this to true, if you need to use | ||
// environment variables inside AoT contexts | ||
// (e.g. forRoot(...) or forChild(...)) | ||
"browserTarget": "your-project-name:build", | ||
"ngsscEnvironmentFile": "src/environments/environment.prod.ts" | ||
"ngsscEnvironmentFile": "src/environments/environment.prod.ts", | ||
// Optional | ||
// (Defaults to the basename of the index option of the browser target) | ||
"filePattern": "index.html" | ||
}, | ||
@@ -170,3 +175,3 @@ "configurations": { | ||
FROM nginx:alpine | ||
ADD https://github.com/kyubisation/angular-server-side-configuration/releases/download/v8.0.0/ngssc_64bit /usr/sbin/ngssc | ||
ADD https://github.com/kyubisation/angular-server-side-configuration/releases/download/v9.0.0-next.0/ngssc_64bit /usr/sbin/ngssc | ||
RUN chmod +x /usr/sbin/ngssc | ||
@@ -173,0 +178,0 @@ COPY dist /usr/share/nginx/html |
@@ -1,7 +0,28 @@ | ||
import { tmpdir } from 'os'; | ||
import commonjs from 'rollup-plugin-commonjs'; | ||
import resolve from 'rollup-plugin-node-resolve'; | ||
import typescript from 'rollup-plugin-typescript2'; | ||
import ts from "@wessberg/rollup-plugin-ts"; | ||
import { join } from 'path'; | ||
export default { | ||
export default ['./builders/ngsscbuild', './schematics/ng-add', './schematics/ng-update'].map(p => ({ | ||
input: join(p, 'index.ts'), | ||
output: { | ||
file: join(p, 'index.js'), | ||
format: 'cjs' | ||
}, | ||
external: [ | ||
'@angular-devkit/architect', | ||
'@angular-devkit/core', | ||
'@angular-devkit/schematics', | ||
'@schematics/angular/utility/change', | ||
'@schematics/angular/utility/config', | ||
'crypto', | ||
'fs', | ||
'path', | ||
'util', | ||
'typescript' | ||
], | ||
plugins: [ | ||
ts({ browserslist: false }) | ||
] | ||
})).concat({ | ||
input: './src/index.ts', | ||
@@ -23,9 +44,6 @@ output: [ | ||
plugins: [ | ||
typescript({ | ||
tsconfigOverride: { compilerOptions: { module: 'ESNext', declaration: false } }, | ||
cacheRoot: `${tmpdir()}/.rpt2_cache_ngssc`, | ||
}), | ||
ts({ browserslist: false }), | ||
resolve(), | ||
commonjs() | ||
] | ||
} | ||
}); |
@@ -8,4 +8,9 @@ { | ||
"factory": "./ng-update/index#updateToV8" | ||
}, | ||
"migration-v9": { | ||
"version": "9-next", | ||
"description": "Updates angular-server-side-configuration to v9", | ||
"factory": "./ng-update/index#updateToV9" | ||
} | ||
} | ||
} |
import { Rule } from '@angular-devkit/schematics'; | ||
import { Schema } from './schema'; | ||
export default function (options: Schema): Rule; | ||
interface Schema { | ||
additionalEnvironmentVariables: string; | ||
/** Name of the project. */ | ||
project: string; | ||
variant: "process" | "NG_ENV"; | ||
ngsscEnvironmentFile: string; | ||
} | ||
declare function indexFunc(options: Schema): Rule; | ||
export { indexFunc as default }; |
@@ -1,9 +0,10 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const core_1 = require("@angular-devkit/core"); | ||
const schematics_1 = require("@angular-devkit/schematics"); | ||
const change_1 = require("@schematics/angular/utility/change"); | ||
const config_1 = require("@schematics/angular/utility/config"); | ||
function default_1(options) { | ||
return schematics_1.chain([ | ||
'use strict'; | ||
var core = require('@angular-devkit/core'); | ||
var schematics = require('@angular-devkit/schematics'); | ||
var change = require('@schematics/angular/utility/change'); | ||
var config = require('@schematics/angular/utility/config'); | ||
function index (options) { | ||
return schematics.chain([ | ||
addNgsscTargetToWorkspace(options), | ||
@@ -15,3 +16,2 @@ addImportAndDescriptionToEnvironmentFile(options), | ||
} | ||
exports.default = default_1; | ||
function addNgsscTargetToWorkspace(options) { | ||
@@ -22,3 +22,3 @@ return (host, context) => { | ||
context.logger.info(`Skipping adding ngsscbuild target to angular.json, as it already exists in project ${projectName}.`); | ||
return schematics_1.noop(); | ||
return schematics.noop(); | ||
} | ||
@@ -30,3 +30,2 @@ architect.ngsscbuild = { | ||
? options.additionalEnvironmentVariables.split(',').map(e => e.trim()) : [], | ||
aotSupport: options.aotSupport, | ||
browserTarget: `${projectName}:build`, | ||
@@ -42,3 +41,3 @@ ngsscEnvironmentFile: options.ngsscEnvironmentFile, | ||
}; | ||
return config_1.updateWorkspace(workspace); | ||
return config.updateWorkspace(workspace); | ||
}; | ||
@@ -49,6 +48,6 @@ } | ||
const { projectRoot } = resolveWorkspace(options, host); | ||
const normalizedPath = core_1.join(core_1.normalize(projectRoot), options.ngsscEnvironmentFile); | ||
const normalizedPath = core.join(core.normalize(projectRoot), options.ngsscEnvironmentFile); | ||
const file = host.get(normalizedPath); | ||
if (!file) { | ||
throw new schematics_1.SchematicsException(`${normalizedPath} does not exist!`); | ||
throw new schematics.SchematicsException(`${normalizedPath} does not exist!`); | ||
} | ||
@@ -81,3 +80,3 @@ else if (file.content.includes('angular-server-side-configuration')) { | ||
`; | ||
const insertion = new change_1.InsertChange(file.path, 0, insertContent); | ||
const insertion = new change.InsertChange(file.path, 0, insertContent); | ||
const recorder = host.beginUpdate(file.path); | ||
@@ -94,3 +93,3 @@ recorder.insertLeft(insertion.pos, insertion.toAdd); | ||
if (buffer === null) { | ||
throw new schematics_1.SchematicsException('Could not find package.json'); | ||
throw new schematics.SchematicsException('Could not find package.json'); | ||
} | ||
@@ -111,3 +110,3 @@ const pkg = { scripts: {}, ...JSON.parse(buffer.toString()) }; | ||
if (!build) { | ||
throw new schematics_1.SchematicsException(`Expected a build target in project ${projectName}!`); | ||
throw new schematics.SchematicsException(`Expected a build target in project ${projectName}!`); | ||
} | ||
@@ -117,3 +116,3 @@ const indexPath = build.options.index || 'src/index.html'; | ||
if (!indexHtml) { | ||
throw new schematics_1.SchematicsException(`Expected index html ${indexPath} to exist!`); | ||
throw new schematics.SchematicsException(`Expected index html ${indexPath} to exist!`); | ||
} | ||
@@ -127,3 +126,3 @@ const indexHtmlContent = indexHtml.content.toString(); | ||
? indexHtmlContent.indexOf('</title>') + 9 : indexHtmlContent.indexOf('</head>'); | ||
const insertion = new change_1.InsertChange(indexHtml.path, insertIndex, ' <!--CONFIG-->\n'); | ||
const insertion = new change.InsertChange(indexHtml.path, insertIndex, ' <!--CONFIG-->\n'); | ||
const recorder = host.beginUpdate(indexHtml.path); | ||
@@ -135,9 +134,11 @@ recorder.insertLeft(insertion.pos, insertion.toAdd); | ||
function resolveWorkspace(options, host) { | ||
const workspace = config_1.getWorkspace(host); | ||
const workspace = config.getWorkspace(host); | ||
const projectName = options.project || workspace.defaultProject || Object.keys(workspace.projects)[0]; | ||
const { architect, root } = workspace.projects[projectName]; | ||
if (!architect) { | ||
throw new schematics_1.SchematicsException(`Expected project ${projectName} to have an architect section!`); | ||
throw new schematics.SchematicsException(`Expected project ${projectName} to have an architect section!`); | ||
} | ||
return { workspace, projectName, architect, projectRoot: root }; | ||
} | ||
module.exports = index; |
@@ -39,8 +39,2 @@ { | ||
}, | ||
"aotSupport": { | ||
"description": "Whether variables are going to be used in AoT contexts (like forRoot(...) or forChild(...))", | ||
"type": "boolean", | ||
"default": true, | ||
"x-prompt": "Are variables going to be used in AoT contexts (like forRoot(...) or forChild(...)):" | ||
}, | ||
"additionalEnvironmentVariables": { | ||
@@ -47,0 +41,0 @@ "description": "Additional environment variables that should be added to ngssc.json", |
import { Rule } from '@angular-devkit/schematics'; | ||
export declare function updateToV8(): Rule; | ||
declare function updateToV8(): Rule; | ||
declare function updateToV9(): Rule; | ||
export { updateToV8, updateToV9 }; |
@@ -1,9 +0,135 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const schematics_1 = require("@angular-devkit/schematics"); | ||
const config_1 = require("@schematics/angular/utility/config"); | ||
const index_1 = __importDefault(require("../ng-add/index")); | ||
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var schematics = require('@angular-devkit/schematics'); | ||
var config = require('@schematics/angular/utility/config'); | ||
var core = require('@angular-devkit/core'); | ||
var change = require('@schematics/angular/utility/change'); | ||
function ngAdd (options) { | ||
return schematics.chain([ | ||
addNgsscTargetToWorkspace(options), | ||
addImportAndDescriptionToEnvironmentFile(options), | ||
addNgsscToPackageScripts(options), | ||
addPlaceholderToIndexHtml(options), | ||
]); | ||
} | ||
function addNgsscTargetToWorkspace(options) { | ||
return (host, context) => { | ||
const { workspace, projectName, architect } = resolveWorkspace(options, host); | ||
if ('ngsscbuild' in architect) { | ||
context.logger.info(`Skipping adding ngsscbuild target to angular.json, as it already exists in project ${projectName}.`); | ||
return schematics.noop(); | ||
} | ||
architect.ngsscbuild = { | ||
builder: 'angular-server-side-configuration:ngsscbuild', | ||
options: { | ||
additionalEnvironmentVariables: options.additionalEnvironmentVariables | ||
? options.additionalEnvironmentVariables.split(',').map(e => e.trim()) : [], | ||
browserTarget: `${projectName}:build`, | ||
ngsscEnvironmentFile: options.ngsscEnvironmentFile, | ||
}, | ||
// tslint:disable-next-line: object-literal-sort-keys | ||
configurations: { | ||
production: { | ||
browserTarget: `${projectName}:build:production`, | ||
}, | ||
}, | ||
}; | ||
return config.updateWorkspace(workspace); | ||
}; | ||
} | ||
function addImportAndDescriptionToEnvironmentFile(options) { | ||
return (host, context) => { | ||
const { projectRoot } = resolveWorkspace(options, host); | ||
const normalizedPath = core.join(core.normalize(projectRoot), options.ngsscEnvironmentFile); | ||
const file = host.get(normalizedPath); | ||
if (!file) { | ||
throw new schematics.SchematicsException(`${normalizedPath} does not exist!`); | ||
} | ||
else if (file.content.includes('angular-server-side-configuration')) { | ||
context.logger.info(`Skipping adding import to ${file.path}, since import was already detected.`); | ||
return; | ||
} | ||
const importExpression = options.variant === 'NG_ENV' | ||
? `import { NG_ENV } from 'angular-server-side-configuration/ng-env';` | ||
: `import 'angular-server-side-configuration/process';`; | ||
const variant = options.variant === 'NG_ENV' ? 'NG_ENV' : 'process.env'; | ||
const insertContent = `${importExpression} | ||
/** | ||
* How to use angular-server-side-configuration: | ||
* | ||
* Use ${variant}.NAME_OF_YOUR_ENVIRONMENT_VARIABLE | ||
* | ||
* export const environment = { | ||
* stringValue: ${variant}.STRING_VALUE, | ||
* stringValueWithDefault: ${variant}.STRING_VALUE || 'defaultValue', | ||
* numberValue: Number(${variant}.NUMBER_VALUE), | ||
* numberValueWithDefault: Number(${variant}.NUMBER_VALUE || 10), | ||
* booleanValue: Boolean(${variant}.BOOLEAN_VALUE), | ||
* booleanValueInverted: ${variant}.BOOLEAN_VALUE_INVERTED !== 'false', | ||
* }; | ||
*/ | ||
`; | ||
const insertion = new change.InsertChange(file.path, 0, insertContent); | ||
const recorder = host.beginUpdate(file.path); | ||
recorder.insertLeft(insertion.pos, insertion.toAdd); | ||
host.commitUpdate(recorder); | ||
}; | ||
} | ||
function addNgsscToPackageScripts(options) { | ||
return (host, context) => { | ||
const { projectName } = resolveWorkspace(options, host); | ||
const pkgPath = '/package.json'; | ||
const buffer = host.read(pkgPath); | ||
if (buffer === null) { | ||
throw new schematics.SchematicsException('Could not find package.json'); | ||
} | ||
const pkg = { scripts: {}, ...JSON.parse(buffer.toString()) }; | ||
if ('build:ngssc' in pkg.scripts) { | ||
context.logger.info(`Skipping adding script to package.json, as it already exists.`); | ||
return; | ||
} | ||
pkg.scripts['build:ngssc'] = `ng run ${projectName}:ngsscbuild:production`; | ||
host.overwrite(pkgPath, JSON.stringify(pkg, null, 2)); | ||
}; | ||
} | ||
function addPlaceholderToIndexHtml(options) { | ||
return (host, context) => { | ||
const { architect, projectName } = resolveWorkspace(options, host); | ||
const build = architect.build; | ||
if (!build) { | ||
throw new schematics.SchematicsException(`Expected a build target in project ${projectName}!`); | ||
} | ||
const indexPath = build.options.index || 'src/index.html'; | ||
const indexHtml = host.get(indexPath); | ||
if (!indexHtml) { | ||
throw new schematics.SchematicsException(`Expected index html ${indexPath} to exist!`); | ||
} | ||
const indexHtmlContent = indexHtml.content.toString(); | ||
if (/<!--\s*CONFIG\s*-->/.test(indexHtmlContent)) { | ||
context.logger.info(`Skipping adding placeholder to ${indexHtml.path}, as it already contains it.`); | ||
return; | ||
} | ||
const insertIndex = indexHtmlContent.includes('</title>') | ||
? indexHtmlContent.indexOf('</title>') + 9 : indexHtmlContent.indexOf('</head>'); | ||
const insertion = new change.InsertChange(indexHtml.path, insertIndex, ' <!--CONFIG-->\n'); | ||
const recorder = host.beginUpdate(indexHtml.path); | ||
recorder.insertLeft(insertion.pos, insertion.toAdd); | ||
host.commitUpdate(recorder); | ||
}; | ||
} | ||
function resolveWorkspace(options, host) { | ||
const workspace = config.getWorkspace(host); | ||
const projectName = options.project || workspace.defaultProject || Object.keys(workspace.projects)[0]; | ||
const { architect, root } = workspace.projects[projectName]; | ||
if (!architect) { | ||
throw new schematics.SchematicsException(`Expected project ${projectName} to have an architect section!`); | ||
} | ||
return { workspace, projectName, architect, projectRoot: root }; | ||
} | ||
const NGSSC_JSON_PATH = '/ngssc.json'; | ||
@@ -14,6 +140,5 @@ function updateToV8() { | ||
const variant = findAndPatchVariantFromFiles(tree); | ||
return schematics_1.chain([ | ||
index_1.default({ | ||
return schematics.chain([ | ||
ngAdd({ | ||
additionalEnvironmentVariables: (ngssc.environmentVariables || []).join(','), | ||
aotSupport: true, | ||
ngsscEnvironmentFile: 'src/environments/environment.prod.ts', | ||
@@ -28,3 +153,25 @@ project: '', | ||
} | ||
exports.updateToV8 = updateToV8; | ||
function updateToV9() { | ||
return (tree, context) => { | ||
const workspace = config.getWorkspace(tree); | ||
context.logger.info(`Removing ngsscbuild entry 'aotSupport', since it is no longer necessary for Ivy.`); | ||
Object.keys(workspace.projects) | ||
.filter(p => workspace.projects[p].architect && | ||
workspace.projects[p].architect.ngsscbuild) | ||
.forEach(p => { | ||
const ngsscbuild = workspace.projects[p].architect.ngsscbuild; | ||
if ('aotSupport' in ngsscbuild.options) { | ||
delete ngsscbuild.options.aotSupport; | ||
context.logger.info(` - Removed from ${p} ngsscbuild options`); | ||
} | ||
Object.keys(ngsscbuild.configurations || {}) | ||
.filter(c => 'aotSupport' in ngsscbuild.configurations[c]) | ||
.forEach(c => { | ||
delete ngsscbuild.configurations[c].aotSupport; | ||
context.logger.info(` - Removed from ${p} ngsscbuild configuration ${c}`); | ||
}); | ||
}); | ||
return config.updateWorkspace(workspace); | ||
}; | ||
} | ||
function tryReadNgsscJson(tree) { | ||
@@ -74,3 +221,3 @@ const ngssc = tree.read(NGSSC_JSON_PATH); | ||
} | ||
const workspace = config_1.getWorkspace(tree); | ||
const workspace = config.getWorkspace(tree); | ||
const projectName = workspace.defaultProject || Object.keys(workspace.projects)[0]; | ||
@@ -81,1 +228,4 @@ context.logger.info('Please remove the ngssc usage from your scripts.'); | ||
} | ||
exports.updateToV8 = updateToV8; | ||
exports.updateToV9 = updateToV9; |
@@ -183,7 +183,10 @@ 'use strict'; | ||
`(function(self){self.process=${JSON.stringify({ env: populatedVariables })};})(window)`; | ||
return `<script>${iife}</script>`; | ||
return `<!--ngssc--><script>${iife}</script><!--/ngssc-->`; | ||
} | ||
function insertIntoHtml(file, iife) { | ||
const fileContent = fs.readFileSync(file, 'utf8'); | ||
if (/<!--\s*CONFIG\s*-->/.test(fileContent)) { | ||
if (/<!--ngssc-->[\w\W]*<!--\/ngssc-->/.test(fileContent)) { | ||
fs.writeFileSync(file, fileContent.replace(/<!--ngssc-->[\w\W]*<!--\/ngssc-->/, iife), 'utf8'); | ||
} | ||
else if (/<!--\s*CONFIG\s*-->/.test(fileContent)) { | ||
fs.writeFileSync(file, fileContent.replace(/<!--\s*CONFIG\s*-->/, iife), 'utf8'); | ||
@@ -190,0 +193,0 @@ } |
@@ -179,7 +179,10 @@ import { readdirSync, lstatSync, existsSync, readFileSync, writeFileSync } from 'fs'; | ||
`(function(self){self.process=${JSON.stringify({ env: populatedVariables })};})(window)`; | ||
return `<script>${iife}</script>`; | ||
return `<!--ngssc--><script>${iife}</script><!--/ngssc-->`; | ||
} | ||
function insertIntoHtml(file, iife) { | ||
const fileContent = readFileSync(file, 'utf8'); | ||
if (/<!--\s*CONFIG\s*-->/.test(fileContent)) { | ||
if (/<!--ngssc-->[\w\W]*<!--\/ngssc-->/.test(fileContent)) { | ||
writeFileSync(file, fileContent.replace(/<!--ngssc-->[\w\W]*<!--\/ngssc-->/, iife), 'utf8'); | ||
} | ||
else if (/<!--\s*CONFIG\s*-->/.test(fileContent)) { | ||
writeFileSync(file, fileContent.replace(/<!--\s*CONFIG\s*-->/, iife), 'utf8'); | ||
@@ -186,0 +189,0 @@ } |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
74632
0200
2.56%7
-30%26
-50%1255
-3.61%1
Infinity%