Original file line number Diff line number Diff line change
@@ -1,67 +1,25 @@
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { Architect } from '@angular-devkit/architect';
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
import { JsonObject, schema } from '@angular-devkit/core';
import { Logger } from '@angular-devkit/core/src/logger';
import { normalize, virtualFs } from '@angular-devkit/core';

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

import { Schema } from './schema';
import { createArchitect, host } from '../../../../test/test-utils';

describe('Ngssc Builder', () => {
let tmpDir: string;
let distDir: string;
let architect: Architect;
let architectHost: TestingArchitectHost;
let logger: Logger;
let logs: string[];
const buildConfig = {
fileReplacements: [{ replace: 'dummy', with: 'environment.prod.ts' }],
index: 'src/index.html',
outputPath: 'dist',
};
const targetSpec = { project: 'app', target: 'ngsscbuild' };
let architect: Architect | undefined;

beforeEach(async () => {
const registry = new schema.CoreSchemaRegistry();
registry.addPostTransform(schema.transforms.addUndefinedDefaults);

tmpDir = mkdtempSync(join(tmpdir(), 'ngssc-'));
writeFileSync(join(tmpDir, 'environment.prod.ts'), envContent, 'utf8');
distDir = join(tmpDir, 'dist');
mkdirSync(distDir);
writeFileSync(join(distDir, 'main.js'), '', 'utf8');
architectHost = new TestingArchitectHost(tmpDir, tmpDir);
architect = new Architect(architectHost, registry);

// This will either take a Node package name, or a path to the directory
// for the package.json file.
await architectHost.addBuilderFromPackage(
'../../../../projects/angular-server-side-configuration'
);
await architectHost.addBuilderFromPackage('..');

logs = [];
logger = new Logger('ngssc');
logger.subscribe((m) => logs.push(m.message));
architect = undefined;
await host.initialize().toPromise();
});
afterEach(async () => host.restore().toPromise());

function addDummyBuildTarget(config: any = buildConfig) {
architectHost.addTarget(
{ project: 'dummy', target: 'build' },
'@angular-devkit/architect:true',
config
);
}
async function runNgsscbuild() {
architect = (await createArchitect(host.root())).architect;

async function runNgsscbuild(options: Schema & JsonObject) {
// A "run" can have multiple outputs, and contains progress information.
const run = await architect.scheduleBuilder(
'angular-server-side-configuration:ngsscbuild',
options,
{ logger }
);
const run = await architect.scheduleTarget(targetSpec);

// The "result" member (of type BuilderOutput) is the next output.
const output = await run.result;
Expand All @@ -73,65 +31,36 @@ describe('Ngssc Builder', () => {
return output;
}

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

return JSON.parse(content);
}

it('should build with process variant', async () => {
addDummyBuildTarget();
const output = await runNgsscbuild({
additionalEnvironmentVariables: [],
aotSupport: false,
browserTarget: 'dummy:build',
filePattern: '',
});
const output = await runNgsscbuild();

expect(output.success).toBe(true);
expect(logs.some((l) => l.includes('ngssc'))).toBeTruthy();
const ngssc: Ngssc = JSON.parse(readFileSync(join(distDir, 'ngssc.json'), 'utf8'));

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

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

expect(output.success).toBe(true);
expect(logs.some((l) => l.includes('ngssc'))).toBeTruthy();
const ngssc: Ngssc = JSON.parse(readFileSync(join(distDir, 'ngssc.json'), 'utf8'));

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

const envContent = `
import 'angular-server-side-configuration/process';
/**
* How to use angular-server-side-configuration:
*
* Use process.env.NAME_OF_YOUR_ENVIRONMENT_VARIABLE
*
* export const environment = {
* stringValue: process.env.STRING_VALUE,
* stringValueWithDefault: process.env.STRING_VALUE || 'defaultValue',
* numberValue: Number(process.env.NUMBER_VALUE),
* numberValueWithDefault: Number(process.env.NUMBER_VALUE || 10),
* booleanValue: Boolean(process.env.BOOLEAN_VALUE),
* booleanValueInverted: process.env.BOOLEAN_VALUE_INVERTED !== 'false',
* };
*/
export const environment = {
production: process.env.PROD !== 'false',
apiBackend: process.env.API_BACKEND || 'http://example.com',
ternary: process.env.TERNARY ? 'asdf' : 'qwer',
simpleValue: process.env.SIMPLE_VALUE,
something: {
asdf: process.env.OMG || 'omg',
qwer: parseInt(process.env.NUMBER || ''),
}
};
`;
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { lstatSync, promises, readdirSync } from 'fs';
import { promises } from 'fs';
import { basename, join } from 'path';
import { BuilderContext, createBuilder, targetFromTargetString } from '@angular-devkit/architect';
import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
import { json, JsonObject } from '@angular-devkit/core';
import { Ngssc } from 'angular-server-side-configuration';
import { BrowserBuilderOptions } from '@angular-devkit/build-angular';
import { glob } from 'glob';
import { Schema } from './schema';
import { VariableDetector } from './variable-detector';
import { NgsscContext } from './ngssc-context';
Expand Down Expand Up @@ -33,15 +34,31 @@ export async function detectVariablesAndBuildNgsscJson(
context: BuilderContext,
multiple: boolean = false
) {
const ngsscContext = await detectVariables(context);
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');
}

export async function detectVariables(context: BuilderContext): Promise<NgsscContext> {
export async function detectVariables(
context: BuilderContext,
searchPattern?: string | null
): Promise<NgsscContext> {
const projectName = context.target && context.target.project;
if (!projectName) {
throw new Error('The builder requires a target.');
}

const projectMetadata = await context.getProjectMetadata(projectName);
const sourceRoot = projectMetadata.sourceRoot as string | undefined;
const defaultSearchPattern = sourceRoot ? `${sourceRoot}/**/!(*server*).ts` : '**/!(*server*).ts';

const detector = new VariableDetector(context.logger);
const typeScriptFiles = findTypeScriptFiles(context.workspaceRoot);
const typeScriptFiles = await 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');
Expand Down Expand Up @@ -73,23 +90,6 @@ export async function detectVariables(context: BuilderContext): Promise<NgsscCon
return ngsscContext;
}

function findTypeScriptFiles(root: string): string[] {
const directory = root.replace(/\\/g, '/');
return readdirSync(directory)
.map((f) => `${directory}/${f}`)
.map((f) => {
const stat = lstatSync(f);
if (stat.isDirectory()) {
return findTypeScriptFiles(f);
} else if (stat.isFile() && f.endsWith('.ts') && !f.endsWith('.spec.ts')) {
return [f];
} else {
return [];
}
})
.reduce((current, next) => current.concat(next), []);
}

export function buildNgssc(
ngsscContext: NgsscContext,
options: NgsscBuildSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
"type": "string",
"description": "The file pattern, into which the environment variables should be inserted during ngssc insert (Defaults to index.html)",
"default": ""
},
"searchPattern": {
"type": "string",
"description": "The search pattern to use when searching for environment variable occurrences (Defaults to {sourceRoot}/**/!(*server*).ts)",
"default": ""
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export interface Schema {
additionalEnvironmentVariables: string[];
browserTarget: string;
filePattern: string | null;
searchPattern?: string | null;
}
3 changes: 2 additions & 1 deletion projects/angular-server-side-configuration/ng-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"dest": "../../dist/angular-server-side-configuration",
"lib": {
"entryFile": "src/public-api.ts"
}
},
"allowedNonPeerDependencies": ["glob"]
}
3 changes: 2 additions & 1 deletion projects/angular-server-side-configuration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@angular/core": "^15.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
"tslib": "^2.3.0",
"glob": "^9.3.1"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ function addDescriptionToMainFile(options: Schema): Rule {
}

const insertFile = [
join(dirname(mainFile), 'environment.prod.ts'),
join(dirname(mainFile), 'environment.ts'),
join(dirname(mainFile), 'app/app.module.ts'),
join(dirname(mainFile), 'app/app.component.ts'),
mainFile,
Expand Down
4 changes: 4 additions & 0 deletions test/hello-world-app/.browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# We want to run tests large with ever green browser so that
# we never trigger differential loading as this will slow down the tests.

last 2 Chrome versions
13 changes: 13 additions & 0 deletions test/hello-world-app/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false
3 changes: 3 additions & 0 deletions test/hello-world-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Don't ignore node_modules, this project is not meant to be installed.
# Also, ~ import path in styles does only looks in the first node_modules found.
# /node_modules
27 changes: 27 additions & 0 deletions test/hello-world-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# HelloWorldApp

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.0-beta.1.

## Development server

Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.

## Code scaffolding

Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.

## Build

Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.

## Running unit tests

Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).

## Running end-to-end tests

Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).

## Further help

To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/main/README.md).
162 changes: 162 additions & 0 deletions test/hello-world-app/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
{
"$schema": "../../../../packages/angular_devkit/core/src/workspace/workspace-schema.json",
"version": 1,
"newProjectRoot": "./projects",
"cli": {
"cache": {
"enabled": false
}
},
"schematics": {},
"projects": {
"app": {
"root": "src",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"targets": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"progress": false,
"sourceMap": false,
"aot": false,
"vendorChunk": true,
"buildOptimizer": false,
"optimization": false,
"extractLicenses": false,
"namedChunks": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
},
"inline-critical-css": {
"optimization": {
"styles": {
"minify": true,
"inlineCritical": true
},
"scripts": true,
"fonts": true
}
}
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist-server",
"main": "src/main.server.ts",
"tsConfig": "src/tsconfig.server.json",
"progress": false,
"sourceMap": true,
"optimization": false,
"extractLicenses": false
}
},
"app-shell": {
"builder": "@angular-devkit/build-angular:app-shell",
"options": {
"browserTarget": "app:build",
"serverTarget": "app:server"
},
"configurations": {
"production": {
"browserTarget": "app:build:production"
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "app:build",
"watch": false
},
"configurations": {
"production": {
"browserTarget": "app:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "app:build",
"progress": false,
"outputPath": "src"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": ["zone.js", "zone.js/testing"],

"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"browsers": "ChromeHeadlessCI",
"progress": false,
"watch": false,
"styles": [
{
"input": "src/styles.css"
}
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
]
}
},
"ngsscbuild": {
"builder": "angular-server-side-configuration:ngsscbuild",
"options": {
"additionalEnvironmentVariables": [],
"browserTarget": "app:build"
},
"configurations": {
"production": {
"browserTarget": "app:build:production"
}
}
}
}
},
"app-e2e": {
"root": "e2e",
"projectType": "application",
"targets": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "protractor.conf.js",
"devServerTarget": "app:serve",
"webdriverUpdate": false
}
}
}
}
}
}
53 changes: 53 additions & 0 deletions test/hello-world-app/karma.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @license
* Copyright Google LLC 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
*/

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

const path = require('path');
process.env.CHROME_BIN = require('puppeteer').executablePath();

module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
},
coverageReporter: {
dir: path.join(__dirname, './coverage'),
subdir: '.',
reporters: [
{type: 'lcov'},
],
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
flags: ['--disable-gpu'],
},
},
singleRun: false,
});
};
43 changes: 43 additions & 0 deletions test/hello-world-app/protractor.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @license
* Copyright Google LLC 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
*/

// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
const { resolve } = require('path');

exports.config = {
allScriptsTimeout: 11000,
specs: ['./e2e/**/*.e2e-spec.ts'],
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: ['--headless', '--disable-gpu', '--window-size=800,600'],
binary: require('puppeteer').executablePath(),
},
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {},
},
onPrepare() {
require('ts-node').register({
project: resolve(__dirname, './e2e/tsconfig.e2e.json'),
});
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY
}
}));
},
};
Empty file.
22 changes: 22 additions & 0 deletions test/hello-world-app/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
</div>
<h2>Here are some links to help you start: </h2>
<ul>
<li>
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
</li>
<li>
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
</li>
</ul>

<p i18n>i18n test</p>

33 changes: 33 additions & 0 deletions test/hello-world-app/src/app/app.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @license
* Copyright Google LLC 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 { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!');
});
});
18 changes: 18 additions & 0 deletions test/hello-world-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license
* Copyright Google LLC 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 { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = process.env.TEST;
}
26 changes: 26 additions & 0 deletions test/hello-world-app/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @license
* Copyright Google LLC 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 { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppComponent } from './app.component';


@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
22 changes: 22 additions & 0 deletions test/hello-world-app/src/app/app.server.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @license
* Copyright Google LLC 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 { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
imports: [
AppModule,
ServerModule,
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
Empty file.
Binary file added test/hello-world-app/src/favicon.ico
Binary file not shown.
14 changes: 14 additions & 0 deletions test/hello-world-app/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>HelloWorldApp</title>
<base href="/">

<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>
9 changes: 9 additions & 0 deletions test/hello-world-app/src/main.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC 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 { AppServerModule } from './app/app.server.module';
15 changes: 15 additions & 0 deletions test/hello-world-app/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @license
* Copyright Google LLC 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 { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';

platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.log(err));
70 changes: 70 additions & 0 deletions test/hello-world-app/src/polyfills.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* @license
* Copyright Google LLC 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
*/

/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/

/***************************************************************************************************
* BROWSER POLYFILLS
*/

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.

/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.

/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/

/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js'; // Included with Angular CLI.

/***************************************************************************************************
* APPLICATION IMPORTS
*/
Binary file added test/hello-world-app/src/spectrum.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions test/hello-world-app/src/src/locale/messages.xlf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="8261100fbc1830397116d0a032e7c8f54af93c68" datatype="html">
<source>i18n test</source>
<context-group purpose="location">
<context context-type="sourcefile">app/app.component.ts</context>
<context context-type="linenumber">21</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/app.component.ts</context>
<context context-type="linenumber">23</context>
</context-group>
</trans-unit>
</body>
</file>
</xliff>
1 change: 1 addition & 0 deletions test/hello-world-app/src/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */
14 changes: 14 additions & 0 deletions test/hello-world-app/src/tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": ["@angular/localize", "node"]
},
"files": [
"main.ts",
"polyfills.ts"
],
"include": [
"**/*.d.ts"
]
}
11 changes: 11 additions & 0 deletions test/hello-world-app/src/tsconfig.server.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "../dist-server",
"baseUrl": "./",
"types": ["@angular/localize", "node"]
},
"files": [
"main.server.ts"
]
}
17 changes: 17 additions & 0 deletions test/hello-world-app/src/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"@angular/localize"
]
},
"files": [
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
13 changes: 13 additions & 0 deletions test/hello-world-app/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license
* Copyright Google LLC 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
*/

/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
id: string;
}
27 changes: 27 additions & 0 deletions test/hello-world-app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2022",
"module": "es2022",
"useDefineForClassFields": false,
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2022",
"dom"
]
},
"angularCompilerOptions": {
"enableIvy": true,
"disableTypeScriptVersionCheck": true,
"strictTemplates": true
}
}
9 changes: 8 additions & 1 deletion test/jasmine.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,12 @@ if (module === require.main) {
const jasmine = new Jasmine({ projectBaseDir: path.resolve() });
jasmine.exitOnCompletion = true;
jasmine.addMatchingSpecFiles(['projects/angular-server-side-configuration/**/*.spec.ts']);
jasmine.execute();
jasmine
.execute()
.then((result) => {
if (result.failedExpectations.length) {
console.error(result.failedExpectations);
}
})
.catch((e) => console.error(e));
}
59 changes: 59 additions & 0 deletions test/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @license
* Copyright Google LLC 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 { Architect, BuilderOutput } from '@angular-devkit/architect';
import { WorkspaceNodeModulesArchitectHost } from '@angular-devkit/architect/node';
import { TestProjectHost, TestingArchitectHost } from '@angular-devkit/architect/testing';
import { Path, getSystemPath, join, normalize, schema, workspaces } from '@angular-devkit/core';

// Default timeout for large specs is 2.5 minutes.
jasmine.DEFAULT_TIMEOUT_INTERVAL = 150000;

export const workspaceRoot = join(normalize(__dirname), `hello-world-app/`);
export const host = new TestProjectHost(workspaceRoot);
export const outputPath: Path = normalize('dist');

export const browserTargetSpec = { project: 'app', target: 'build' };
export const devServerTargetSpec = { project: 'app', target: 'serve' };
export const extractI18nTargetSpec = { project: 'app', target: 'extract-i18n' };
export const karmaTargetSpec = { project: 'app', target: 'test' };
export const tslintTargetSpec = { project: 'app', target: 'lint' };
export const protractorTargetSpec = { project: 'app-e2e', target: 'e2e' };

export async function createArchitect(workspaceRoot: Path) {
const registry = new schema.CoreSchemaRegistry();
registry.addPostTransform(schema.transforms.addUndefinedDefaults);
const workspaceSysPath = getSystemPath(workspaceRoot);

const { workspace } = await workspaces.readWorkspace(
workspaceSysPath,
workspaces.createWorkspaceHost(host)
);
const architectHost = new TestingArchitectHost(
workspaceSysPath,
workspaceSysPath,
new WorkspaceNodeModulesArchitectHost(workspace, workspaceSysPath)
);
await architectHost.addBuilderFromPackage('..');
//require('ts-node').register(require('../projects/angular-server-side-configuration/builders/tsconfig.json'));
await architectHost.addBuilderFromPackage(
'../../../../dist/angular-server-side-configuration'
);
const architect = new Architect(architectHost, registry);

return {
workspace,
architectHost,
architect,
};
}

export interface BrowserBuildOutput {
output: BuilderOutput;
files: { [file: string]: Promise<string> };
}
35 changes: 35 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4363,6 +4363,16 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7:
once "^1.3.0"
path-is-absolute "^1.0.0"

glob@^9.3.1:
version "9.3.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.1.tgz#4a15cf72479a440f77e97805b21e1b5b6e4fb18a"
integrity sha512-qERvJb7IGsnkx6YYmaaGvDpf77c951hICMdWaFXyH3PlVob8sbPJJyJX0kWkiCWyXUzoy9UOTNjGg0RbD8bYIw==
dependencies:
fs.realpath "^1.0.0"
minimatch "^7.4.1"
minipass "^4.2.4"
path-scurry "^1.6.1"

globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
Expand Down Expand Up @@ -5433,6 +5443,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"

lru-cache@^7.14.1:
version "7.18.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==

lru-cache@^7.4.4, lru-cache@^7.5.1, lru-cache@^7.7.1:
version "7.14.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.1.tgz#8da8d2f5f59827edb388e63e459ac23d6d408fea"
Expand Down Expand Up @@ -5644,6 +5659,13 @@ minimatch@^5.0.1:
dependencies:
brace-expansion "^2.0.1"

minimatch@^7.4.1:
version "7.4.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f"
integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA==
dependencies:
brace-expansion "^2.0.1"

minimatch@~3.0.4:
version "3.0.8"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.8.tgz#5e6a59bd11e2ab0de1cfb843eb2d82e546c321c1"
Expand Down Expand Up @@ -5730,6 +5752,11 @@ minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6:
dependencies:
yallist "^4.0.0"

minipass@^4.0.2, minipass@^4.2.4:
version "4.2.5"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb"
integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==

minizlib@^2.1.1, minizlib@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
Expand Down Expand Up @@ -6367,6 +6394,14 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==

path-scurry@^1.6.1:
version "1.6.2"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.2.tgz#72783838113a4a0cec155323b2c01eb1b193396f"
integrity sha512-J6MQNh56h6eHFY3vsQ+Lq+zKPwn71POieutmVt2leU8W+zz8HVIdJyn3I3Zs6IKbIQtuKXirVjTBFNBcbFO44Q==
dependencies:
lru-cache "^7.14.1"
minipass "^4.0.2"

path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
Expand Down