Skip to content

Commit

Permalink
feat: enable VariableDetector to also detect index access usages
Browse files Browse the repository at this point in the history
BREAKING CHANGE: This also refactors the ngsscbuild builder extensively.
This breaking change will only affect you, if you used the internals of this library.
  • Loading branch information
kyubisation committed Nov 13, 2021
1 parent 86d12df commit 702dac3
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 231 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,90 @@
import { BuilderContext, createBuilder } from '@angular-devkit/architect';
import { JsonObject } from '@angular-devkit/core';

import { NgsscBuilder } from './ngssc-builder';
import { promises } from 'fs';
import { basename, join } from 'path';
import { BuilderContext, createBuilder, targetFromTargetString } from '@angular-devkit/architect';
import { json, JsonObject } from '@angular-devkit/core';
import { Ngssc } from 'angular-server-side-configuration';
import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
import { Schema } from './schema';
import { VariableDetector } from './variable-detector';
import { NgsscContext } from './ngssc-context';

export type NgsscBuildSchema = Schema;

const readFileAsync = promises.readFile;
const writeFileAsync = promises.writeFile;

export { Schema as NgsscBuildSchema } from './schema';
export async function ngsscBuild(options: NgsscBuildSchema, context: BuilderContext) {
const browserTarget = targetFromTargetString(options.browserTarget);
const rawBrowserOptions = await context.getTargetOptions(browserTarget);
const browserName = await context.getBuilderNameForTarget(browserTarget);
const browserOptions = await context.validateOptions<json.JsonObject & BrowserBuilderOptions>(
rawBrowserOptions,
browserName
);
const scheduledTarget = await context.scheduleTarget(browserTarget);
const result = await scheduledTarget.result;
await detectVariablesAndBuildNgsscJson(options, browserOptions, context);
return result;
}

export async function detectVariablesAndBuildNgsscJson(
options: NgsscBuildSchema,
browserOptions: BrowserBuilderOptions,
context: BuilderContext
) {
const ngsscContext = await detectVariables(options, context);
const outputPath = join(context.workspaceRoot, browserOptions.outputPath);
const ngssc = buildNgssc(ngsscContext, options, browserOptions);
await writeFileAsync(join(outputPath, 'ngssc.json'), JSON.stringify(ngssc, null, 2), 'utf8');
}

export async function detectVariables(
options: NgsscBuildSchema,
context: BuilderContext
): Promise<NgsscContext> {
const environmentVariableFile = join(context.workspaceRoot, options.ngsscEnvironmentFile);
const detector = new VariableDetector(context.logger);
const fileContent = await readFileAsync(environmentVariableFile, 'utf8');
const ngsscContext = detector.detect(fileContent);
context.logger.info(
`ngssc: Detected variant '${ngsscContext.variant}' with variables ` +
`'${ngsscContext.variables.join(', ')}'`
);
if (ngsscContext.variant === 'NG_ENV') {
context.logger.warn(
'Variant NG_ENV is deprecated and will be removed with version 14. ' +
'Please change usage to `process.env`.'
);
}

return ngsscContext;
}

export function buildNgssc(
ngsscContext: NgsscContext,
options: NgsscBuildSchema,
browserOptions?: BrowserBuilderOptions
): Ngssc {
return {
environmentVariables: [
...ngsscContext.variables,
...(options.additionalEnvironmentVariables || []),
],
filePattern: options.filePattern || extractFilePattern(browserOptions?.index),
variant: ngsscContext.variant,
};
}

export async function ngsscBuild(options: Schema, context: BuilderContext) {
return await new NgsscBuilder(options, context).run();
function extractFilePattern(index: BrowserBuilderOptions['index'] | undefined) {
if (!index) {
return '**/index.html';
} else if (typeof index === 'string') {
return basename(index);
} else if (index.output) {
return basename(index.output);
} else {
return basename(index.input);
}
}

export default createBuilder<Schema & JsonObject>(ngsscBuild);
export default createBuilder<NgsscBuildSchema & JsonObject>(ngsscBuild);

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { Variant } from 'angular-server-side-configuration';

/**
* The context for ngssc.
* @public
*/
/** The context for ngssc. */
export interface NgsscContext {
variant: Variant;
variantImport: string | undefined;
variables: { variable: string; expression: string }[];
variables: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
},
"ngsscEnvironmentFile": {
"type": "string",
"description": "The ngssc environment file (usually src/environments/environment.prod.ts)"
"description": "The ngssc environment file (usually src/environments/environment.prod.ts)",
"default": "src/environments/environment.prod.ts"
},
"filePattern": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,32 @@
import { NgsscContext } from './ngssc-context';

import { VariableDetector } from './variable-detector';

describe('VariableDetector', () => {
const expectedNgEnvVariables: { [variable: string]: string } = {
API_BACKEND: `NG_ENV.API_BACKEND || 'http://example.com'`,
NUMBER: `parseInt(NG_ENV.NUMBER || '')`,
OMG: `NG_ENV.OMG || 'omg'`,
PROD: `NG_ENV.PROD !== 'false'`,
SIMPLE_VALUE: `NG_ENV.SIMPLE_VALUE`,
TERNARY: `NG_ENV.TERNARY ? 'asdf' : 'qwer'`,
};

function findVariableByName(result: NgsscContext, variable: string) {
return result.variables.filter((v) => v.variable === variable).map((v) => v.expression)[0];
}
const expectedEnvVariables = [
'API_BACKEND',
'INDEX_ACCESS',
'INDEX_ACCESS2',
'NUMBER',
'OMG',
'PROD',
'SIMPLE_VALUE',
'TERNARY',
];

it('should detect process.env variables', () => {
const detector = new VariableDetector();
const expectedVariables: { [variable: string]: string } = {
API_BACKEND: `process.env.API_BACKEND || 'http://example.com'`,
NUMBER: `parseInt(process.env.NUMBER || '')`,
OMG: `process.env.OMG || 'omg'`,
PROD: `process.env.PROD !== 'false'`,
SIMPLE_VALUE: `process.env.SIMPLE_VALUE`,
TERNARY: `process.env.TERNARY ? 'asdf' : 'qwer'`,
};

const result = detector.detect(envContent);
expect(result.variant).toBe('process');
expect(result.variables.length).toBe(6);
for (const variable of Object.keys(expectedVariables)) {
expect(findVariableByName(result, variable)).toBe(expectedVariables[variable]);
}
expect(result.variables.length).toBe(8);
expect(result.variables).toEqual(expectedEnvVariables);
});

it('should detect NG_ENV variables with ng-env', () => {
const detector = new VariableDetector();
const result = detector.detect(envContentNgEnv);
expect(result.variant).toBe('NG_ENV');
expect(result.variantImport).toBe(
`import { NG_ENV } from 'angular-server-side-configuration/ng-env';`
);
expect(result.variables.length).toBe(6);
for (const variable of Object.keys(expectedNgEnvVariables)) {
expect(findVariableByName(result, variable)).toBe(expectedNgEnvVariables[variable]);
}
});

it('should fail variant detection with wrong import', () => {
const detector = new VariableDetector();
expect(() => detector.detect(`import 'angular-server-side-configuration/failure';`)).toThrow();
expect(result.variables.length).toBe(8);
expect(result.variables).toEqual(expectedEnvVariables);
});
});

Expand Down Expand Up @@ -80,7 +56,9 @@ export const environment = {
something: {
asdf: NG_ENV.OMG || 'omg',
qwer: parseInt(NG_ENV.NUMBER || ''),
}
},
indexAccess: NG_ENV['INDEX_ACCESS'],
indexAccess2: NG_ENV[\`INDEX_ACCESS2\`],
};
`;

Expand Down Expand Up @@ -110,6 +88,8 @@ export const environment = {
something: {
asdf: process.env.OMG || 'omg',
qwer: parseInt(process.env.NUMBER || ''),
}
},
indexAccess: process.env['INDEX_ACCESS'],
indexAccess2: process.env[\`INDEX_ACCESS2\`],
};
`;
Loading

0 comments on commit 702dac3

Please sign in to comment.