Skip to content

Commit

Permalink
fix: adapt to @angular-devkit/build-angular:application builder
Browse files Browse the repository at this point in the history
  • Loading branch information
kyubisation committed Nov 12, 2023
1 parent 47015b2 commit d89a225
Show file tree
Hide file tree
Showing 25 changed files with 857 additions and 106 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@
"@angular/forms": "^17.0.1",
"@angular/platform-browser": "^17.0.1",
"@angular/platform-browser-dynamic": "^17.0.1",
"@angular/platform-server": "^17.0.1",
"@angular/router": "^17.0.1",
"@angular/ssr": "^17.0.0",
"rxjs": "7.8.1",
"tslib": "^2.6.2",
"zone.js": "~0.14.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import { Architect } from '@angular-devkit/architect';
import { TestProjectHost } from '@angular-devkit/architect/testing';
import { normalize, virtualFs } from '@angular-devkit/core';

import { Ngssc } from 'angular-server-side-configuration';

import { createArchitect, host } from '../../../../test/test-utils';
import { applicationHost, createArchitect, legacyHost } from '../../../../test/test-utils';

describe('Ngssc Builder', () => {
const targetSpec = { project: 'app', target: 'ngsscbuild' };
let architect: Architect | undefined;

beforeEach(async () => {
architect = undefined;
await host.initialize().toPromise();
});
afterEach(async () => host.restore().toPromise());

async function runNgsscbuild() {
architect = (await createArchitect(host.root())).architect;
async function runNgsscbuild(host: TestProjectHost) {
architect = (await createArchitect(host.root(), host)).architect;

// A "run" can have multiple outputs, and contains progress information.
const run = await architect.scheduleTarget(targetSpec);
Expand All @@ -31,36 +26,90 @@ describe('Ngssc Builder', () => {
return output;
}

function readNgsscJson(): Ngssc {
const content = virtualFs.fileBufferToString(
host.scopedSync().read(normalize('dist/ngssc.json')),
);
function readNgsscJson(host: TestProjectHost, ngsscPath = 'dist/ngssc.json'): Ngssc {
const content = virtualFs.fileBufferToString(host.scopedSync().read(normalize(ngsscPath)));

return JSON.parse(content);
}

it('should build with process variant', async () => {
const output = await runNgsscbuild();

expect(output.success).toBe(true);

const ngssc = readNgsscJson();
expect(ngssc.variant).toEqual('process');
expect(ngssc.filePattern).toEqual('index.html');
describe('@angular-devkit/build-angular:browser', () => {
beforeEach(async () => {
architect = undefined;
await legacyHost.initialize().toPromise();
});
afterEach(async () => legacyHost.restore().toPromise());

it('should build with process variant', async () => {
const output = await runNgsscbuild(legacyHost);

expect(output.success).toBe(true);

const ngssc = readNgsscJson(legacyHost);
expect(ngssc.variant).toEqual('process');
expect(ngssc.filePattern).toEqual('index.html');
});

it('should aggregate environment variables', async () => {
const expected = 'OTHER_VARIABLE';
legacyHost.replaceInFile(
'angular.json',
'"additionalEnvironmentVariables": [],',
`"additionalEnvironmentVariables": ["${expected}"],`,
);
const output = await runNgsscbuild(legacyHost);

expect(output.success).toBe(true);

const ngssc = readNgsscJson(legacyHost);
expect(ngssc.environmentVariables).toContain(expected);
});
});

it('should aggregate environment variables', async () => {
const expected = 'OTHER_VARIABLE';
host.replaceInFile(
'angular.json',
'"additionalEnvironmentVariables": [],',
`"additionalEnvironmentVariables": ["${expected}"],`,
);
const output = await runNgsscbuild();

expect(output.success).toBe(true);

const ngssc = readNgsscJson();
expect(ngssc.environmentVariables).toContain(expected);
describe('@angular-devkit/build-angular:application', () => {
beforeEach(async () => {
architect = undefined;
await applicationHost.initialize().toPromise();
});
afterEach(async () => applicationHost.restore().toPromise());

it('should build with process variant', async () => {
const output = await runNgsscbuild(applicationHost);

expect(output.success).toBe(true);

const ngssc = readNgsscJson(applicationHost);
expect(ngssc.variant).toEqual('process');
expect(ngssc.filePattern).toEqual('**/index{.,.server.}html');
});

it('should aggregate environment variables', async () => {
const expected = 'OTHER_VARIABLE';
applicationHost.replaceInFile(
'angular.json',
'"additionalEnvironmentVariables": [],',
`"additionalEnvironmentVariables": ["${expected}"],`,
);
const output = await runNgsscbuild(applicationHost);

expect(output.success).toBe(true);

const ngssc = readNgsscJson(applicationHost);
expect(ngssc.environmentVariables).toContain(expected);
});

it('should write ngssc into dist/browser directory without SSR', async () => {
applicationHost.replaceInFile(
'angular.json',
/("server":\s+"src\/main.server.ts",|"prerender":\s+true,|"ssr":\s+\{\s+"entry":\s+"server.ts"\s+\})/g,
'',
);
const output = await runNgsscbuild(applicationHost);

expect(output.success).toBe(true);

const ngssc = readNgsscJson(applicationHost, 'dist/browser/ngssc.json');
expect(ngssc.variant).toEqual('process');
expect(ngssc.filePattern).toEqual('index.html');
});
});
});
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import { promises } from 'fs';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { basename, join } from 'path';
import { BuilderContext, createBuilder, targetFromTargetString } from '@angular-devkit/architect';
import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
import { ApplicationBuilderOptions, BrowserBuilderOptions } from '@angular-devkit/build-angular';
import { json, JsonObject } from '@angular-devkit/core';
import { Ngssc } from 'angular-server-side-configuration';
import { glob } from 'glob';
import * as glob from 'glob';
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;
type BuilderOptions = ApplicationBuilderOptions | BrowserBuilderOptions;
type ApplicationBuilderVariant = undefined | 'browser-only' | 'server';

export async function ngsscBuild(options: NgsscBuildSchema, context: BuilderContext) {
const buildTarget = targetFromTargetString(options.buildTarget || options.browserTarget);
const rawBrowserOptions = await context.getTargetOptions(buildTarget);
const browserName = await context.getBuilderNameForTarget(buildTarget);
const browserOptions = await context.validateOptions<json.JsonObject & BrowserBuilderOptions>(
rawBrowserOptions,
browserName,
const rawBuilderOptions = await context.getTargetOptions(buildTarget);
const builderName = await context.getBuilderNameForTarget(buildTarget);
const builderOptions = await context.validateOptions<json.JsonObject & BuilderOptions>(
rawBuilderOptions,
builderName,
);
const scheduledTarget = await context.scheduleTarget(buildTarget);
const result = await scheduledTarget.result;
Expand All @@ -32,20 +31,43 @@ export async function ngsscBuild(options: NgsscBuildSchema, context: BuilderCont
return result;
}

await detectVariablesAndBuildNgsscJson(options, browserOptions, context);
await detectVariablesAndBuildNgsscJson(
options,
builderOptions,
context,
false,
builderName !== '@angular-devkit/build-angular:application'
? undefined
: 'server' in builderOptions && builderOptions.server
? 'server'
: 'browser-only',
);

return result;
}

export async function detectVariablesAndBuildNgsscJson(
options: NgsscBuildSchema,
browserOptions: BrowserBuilderOptions,
builderOptions: BuilderOptions,
context: BuilderContext,
multiple: boolean = false,
applicationBuilderVariant: ApplicationBuilderVariant = undefined,
) {
const ngsscContext = await detectVariables(context, options.searchPattern);
const outputPath = join(context.workspaceRoot, browserOptions.outputPath);
const ngssc = buildNgssc(ngsscContext, options, browserOptions, multiple);
await writeFileAsync(join(outputPath, 'ngssc.json'), JSON.stringify(ngssc, null, 2), 'utf8');
let outputPath = join(context.workspaceRoot, builderOptions.outputPath);
const ngssc = buildNgssc(
ngsscContext,
options,
builderOptions,
multiple,
applicationBuilderVariant,
);

const browserOutputPath = join(outputPath, 'browser');
if (applicationBuilderVariant === 'browser-only' && existsSync(browserOutputPath)) {
outputPath = browserOutputPath;
}
writeFileSync(join(outputPath, 'ngssc.json'), JSON.stringify(ngssc, null, 2), 'utf8');
}

export async function detectVariables(
Expand All @@ -64,14 +86,14 @@ export async function detectVariables(
: '**/environments/environment*.ts';

const detector = new VariableDetector(context.logger);
const typeScriptFiles = await glob(searchPattern || defaultSearchPattern, {
const typeScriptFiles = await glob.glob(searchPattern || defaultSearchPattern, {
absolute: true,
cwd: context.workspaceRoot,
ignore: ['**/node_modules/**', '**/*.spec.ts', '**/*.d.ts'],
});
let ngsscContext: NgsscContext | null = null;
for (const file of typeScriptFiles) {
const fileContent = await readFileAsync(file, 'utf8');
const fileContent = readFileSync(file, 'utf8');
const innerNgsscContext = detector.detect(fileContent);
if (!innerNgsscContext.variables.length) {
continue;
Expand Down Expand Up @@ -103,22 +125,34 @@ export async function detectVariables(
export function buildNgssc(
ngsscContext: NgsscContext,
options: NgsscBuildSchema,
browserOptions?: BrowserBuilderOptions,
builderOptions?: BuilderOptions,
multiple: boolean = false,
applicationBuilderVariant: ApplicationBuilderVariant = undefined,
): Ngssc {
return {
environmentVariables: [
...ngsscContext.variables,
...(options.additionalEnvironmentVariables || []),
],
filePattern: options.filePattern || extractFilePattern(browserOptions?.index, multiple),
filePattern:
options.filePattern ||
extractFilePattern(builderOptions, multiple, applicationBuilderVariant),
variant: ngsscContext.variant,
};
}

function extractFilePattern(index: BrowserBuilderOptions['index'] | undefined, multiple: boolean) {
function extractFilePattern(
builderOptions: BuilderOptions | undefined,
multiple: boolean,
applicationBuilderVariant: ApplicationBuilderVariant = undefined,
) {
if (builderOptions && applicationBuilderVariant === 'server') {
return '**/index{.,.server.}html';
}

const index = builderOptions?.index;
let result = '**/index.html';
if (!index) {
if (!index || typeof index === 'boolean') {
return result;
} else if (typeof index === 'string') {
result = basename(index);
Expand Down
4 changes: 2 additions & 2 deletions scripts/build-lib.mts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ async function finalizePackage() {
}

const packageJsonPath = join(targetDir, 'package.json');
const distPackageJson = JSON.parse(readFileSync(join(rootDir, packageJsonPath), 'utf8'));
const distPackageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
distPackageJson.sideEffects = glob
.sync(['esm*/**/public_api.{mjs,js}', 'fesm*/*{ng-env,process}.{mjs,js}'], {
cwd: targetDir,
dotRelative: true,
})
.sort();
writeFileSync(join(rootDir, packageJsonPath), JSON.stringify(distPackageJson, null, 2), 'utf8');
writeFileSync(packageJsonPath, JSON.stringify(distPackageJson, null, 2), 'utf8');
}

function walk(root: string | string[], fileRegex: RegExp): string[] {
Expand Down
45 changes: 0 additions & 45 deletions test/hello-world-app/protractor.conf.js

This file was deleted.

Loading

0 comments on commit d89a225

Please sign in to comment.