Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: easily override default config for build #676

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/development.yml
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@ jobs:
run: git diff --exit-code --raw -p --stat

- name: Compile Angular
run: npm run ng -- build --aot
run: npm run build client

- name: Compile Unit Tests
run: npx tsc -p tsconfig.all.json
2 changes: 1 addition & 1 deletion .github/workflows/windows.yml
Original file line number Diff line number Diff line change
@@ -55,4 +55,4 @@ jobs:
node scripts/init-local-environment -f

- name: Build with local environment
run: npm run ng -- build -c local
run: npm run build client --configuration=local
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -9,21 +9,22 @@ COPY projects/requisition-management/src/app /workspace/projects/requisition-man
COPY src /workspace/src/
COPY tsconfig.app.json tsconfig.app-no-checks.json tsconfig.json ngsw-config.json .browserslistrc angular.json /workspace/
RUN npm run build:schematics && npm run synchronize-lazy-components -- --ci
ARG configuration=production
ARG configuration
RUN test -z "${configuration}" || npm config set intershop-pwa:default-build-configuration ${configuration}
COPY scripts /workspace/scripts/
RUN test "${configuration}" = 'local' && node scripts/init-local-environment.js || true
ARG serviceWorker
RUN node schematics/customization/service-worker ${serviceWorker} || true
COPY templates/webpack/* /workspace/templates/webpack/
ARG testing=false
ENV TESTING=${testing}
RUN npm run ng -- build -c ${configuration}
RUN npm run build client -- --deploy-url=DEPLOY_URL_PLACEHOLDER
# synchronize-marker:pwa-docker-build:end

# ^ this part above is copied to Dockerfile_noSSR and should be kept in sync

COPY tsconfig.server.json server.ts /workspace/
RUN npm run ng -- run intershop-pwa:server -c ${configuration}
RUN npm run build server
# remove cache check for resources (especially index.html)
# https://github.com/angular/angular/issues/23613#issuecomment-415886919
RUN test "${serviceWorker}" = "true" && sed -i 's/canonicalHash !== cacheBustedHash/false/g' /workspace/dist/browser/ngsw-worker.js || true
8 changes: 6 additions & 2 deletions Dockerfile_noSSR
Original file line number Diff line number Diff line change
@@ -9,19 +9,23 @@ COPY projects/requisition-management/src/app /workspace/projects/requisition-man
COPY src /workspace/src/
COPY tsconfig.app.json tsconfig.app-no-checks.json tsconfig.json ngsw-config.json .browserslistrc angular.json /workspace/
RUN npm run build:schematics && npm run synchronize-lazy-components -- --ci
ARG configuration=production
ARG configuration
RUN test -z "${configuration}" || npm config set intershop-pwa:default-build-configuration ${configuration}
COPY scripts /workspace/scripts/
RUN test "${configuration}" = 'local' && node scripts/init-local-environment.js || true
ARG serviceWorker
RUN node schematics/customization/service-worker ${serviceWorker} || true
COPY templates/webpack/* /workspace/templates/webpack/
ARG testing=false
ENV TESTING=${testing}
RUN npm run ng -- build -c ${configuration}
RUN npm run build client -- --deploy-url=DEPLOY_URL_PLACEHOLDER
# synchronize-marker:pwa-docker-build:end

# ^ this part above is copied from the standard Dockerfile and should be kept in sync

ARG deployUrl=/
RUN npx ts-node scripts/set-deploy-url ${deployUrl}

FROM nginx:alpine
COPY templates/nginx.conf /etc/nginx/nginx.conf
COPY --from=buildstep /workspace/dist/browser /usr/share/nginx/html
4 changes: 2 additions & 2 deletions Dockerfile_reports
Original file line number Diff line number Diff line change
@@ -9,8 +9,8 @@ RUN npx license-checker --csv --out reports/licenses/licenses.csv --customPath t
RUN npm run compodoc
RUN npm i --no-save jscpd-html-reporter && node reports/jscpd-report
RUN npm i -g webpack-bundle-analyzer
RUN rm -Rf dist && npx ng build --progress false --aot --stats-json && webpack-bundle-analyzer dist/browser/stats.json dist/browser -r reports/bundle_aot/index.html -m static
RUN rm -Rf dist && npx ng build --progress false --prod --stats-json && webpack-bundle-analyzer dist/browser/stats.json dist/browser -r reports/bundle_prod/index.html -m static
RUN rm -Rf dist && npm run build client -- --progress false --aot --stats-json && webpack-bundle-analyzer dist/browser/stats.json dist/browser -r reports/bundle_aot/index.html -m static
RUN rm -Rf dist && npm run build client -- --progress false --prod --stats-json && webpack-bundle-analyzer dist/browser/stats.json dist/browser -r reports/bundle_prod/index.html -m static

FROM danjellz/http-server
COPY --from=reporting /workspace/reports /public
26 changes: 22 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -18,10 +18,32 @@ services:
# - PROXY_ICM=true
- TRUST_ICM=true
# - PROMETHEUS=on

# <CDN-Example>
# add 127.0.0.1 mypwa.net to your hosts file and
# uncomment the following lines
#
# - DEPLOY_URL=http://mypwa.net:4222
# cdn:
# build:
# context: .
# dockerfile: Dockerfile_noSSR
# args:
# configuration: production
# serviceWorker: 'false'
# deployUrl: http://mypwa.net:4222
# ports:
# - '4222:4200'
#
# </CDN-Example>

nginx:
build: nginx
depends_on:
- pwa
ports:
- '4200:80'
# - '9113:9113'
environment:
UPSTREAM_PWA: 'http://pwa:4200'
# DEBUG: 1
@@ -54,7 +76,3 @@ services:
application: smb-responsive
features: quoting
theme: "blue|688dc3"

ports:
- '4200:80'
# - '9113:9113'
28 changes: 14 additions & 14 deletions docs/concepts/configuration.md
Original file line number Diff line number Diff line change
@@ -219,21 +219,21 @@ To dynamically set the default locale, use the URL parameter `lang` when rewriti

## Extend Locales

To add other languages except English, German or French, you have to create a new json-mapping-file with all translations, e.g., _./src/assets/i18n/nl_NL.json_).
Add the locale in the file _./src/environment/environments.ts_.
Additionally, for Angular's built-in components, e.g., currency-pipe, you have to register locale data similar to `localeDe` and `localeFr` with `registerLocaleData(localeNl)` in _./src/app/core/configuration.module.ts._
To add other languages except English, German or French:

```typescript
...
import localeNl from '@angular/common/locales/nl';
...
export class ConfigurationModule {
constructor(@Inject(LOCALE_ID) lang: string, translateService: TranslateService) {
registerLocaleData(localeNl);
...
}
}
```
1. Create a new json-mapping-file with all translations, e.g., `src/assets/i18n/nl_NL.json`.

2. Add the locale to the environments under `src/environments`, e.g.

```typescript
{ lang: 'nl_NL', currency: 'EUR', value: 'nl', displayName: 'Dutch', displayLong: 'Dutch (Netherlands)' }
```

3. Import a [global variant of the locale data](https://angular.io/guide/i18n#import-global-variants-of-the-locale-data) in the [`InternationalizationModule`](../../src/app/core/internationalization.module.ts), e.g.
```typescript
case 'nl_NL':
return import('@angular/common/locales/global/nl');
```

# Further References

2 changes: 1 addition & 1 deletion docs/concepts/deployment-angular.md
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ kb_sync_latest_only
## Simple Browser-Side Rendering

Angular applications are built for a [static deployment][angular-deployment] by default.
When running `ng build` in any shape or form, the build output is aggregated in the `dist` folder and the resulting files can be statically served using any HTTP server that is capable of doing that (only a [fallback](https://angular.io/guide/deployment#routed-apps-must-fallback-to-indexhtml) for `index.html` has to be configured).
When running `npm run build client` in any shape or form, the build output is aggregated in the `dist` folder and the resulting files can be statically served using any HTTP server that is capable of doing that (only a [fallback](https://angular.io/guide/deployment#routed-apps-must-fallback-to-indexhtml) for `index.html` has to be configured).

When the application is served this way, the initial page response for the browser is minimal (`index.html` with scripts inserted) and the application gets composed and rendered on the client side.

5 changes: 2 additions & 3 deletions docs/guides/continuous-integration.md
Original file line number Diff line number Diff line change
@@ -16,10 +16,9 @@ This section provides an overview of required continuous integration steps to ve
Since Angular projects are JavaScript-based, even though they use TypeScript-based code, everything is highly dynamic.
Parts of the software can still run error free with `webpack-dev-server` (`ng serve`), even if other parts were not compiled or have template errors.

To ensure having a consistent code base, the CI system should always perform at least an ahead-of-time compile step (`ng build --aot`).

To ensure having a consistent code base, the CI system should always perform at least an ahead-of-time compile step.
Angular in production mode does AoT and applies some more code optimizations that can sometimes clash with definitions or third-party libraries.
To catch this, a production build should be performed: `ng build --prod`.
To catch this, a production build should be performed: `npm run build`.

To check the integrity of the unit tests, the TypeScript compiler can be used: `npx tsc -p src/tsconfig.spec.json`.

2 changes: 1 addition & 1 deletion docs/guides/getting-started.md
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ Deployments are generated to the _dist_ folder of the project.
Use `npm run build` to generate an Angular Universal enabled version.
On the server the _dist/server.js_ script has to be executed with `node`.

Alternatively, you can use `ng build --prod` to get an application using browser rendering.
Alternatively, you can use `npm run build client` to get an application using browser rendering.
All the files under `dist/browser` have to be served statically.
The server has to be configured for fallback routing,
see [Server Configuration in Angular Docs](https://angular.io/guide/deployment#server-configuration).
5 changes: 5 additions & 0 deletions docs/guides/ssr-startup.md
Original file line number Diff line number Diff line change
@@ -18,6 +18,11 @@ By default the `production` configuration is built.

All `configuration` options can also be multiple configurations like `--configuration=brand,production`, where production should always come last.

If no configuration is supplied, the one defined in the `package.json` under `config.default-build-configuration` is used as a fallback.
That way you can also override the default for project customizations.

You can also override the default locally via `npm config set intershop-pwa:default-build-configuration <configuration>`.

## Running

Overwriting configurations of the PWA is entirely done by environment variables.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -36,9 +36,9 @@
"compodoc": "npx @compodoc/compodoc -p tsconfig.app.json -d docs/compodoc -y docs/theme --hideGenerator",
"compodoc:serve": "npm run compodoc -- -s -w",
"synchronize-lazy-components": "ng g lazy-components",
"build:watch": "ng build --aot --watch",
"build:watch": "npm run build client -- --aot --watch",
"build": "node scripts/build-pwa",
"analyze": "ng build --prod --stats-json && npx webpack-bundle-analyzer --host 0.0.0.0 dist/browser/stats.json dist/browser",
"analyze": "npm run build client -- --stats-json && npx webpack-bundle-analyzer --host 0.0.0.0 dist/browser/stats.json dist/browser",
"serve": "node dist/server/main.js",
"start": "npm-run-all build serve",
"start:local": "ng run intershop-pwa:serve-ssr:local"
@@ -187,7 +187,8 @@
"config": {
"commitizen": {
"path": "./node_modules/cz-customizable"
}
},
"default-build-configuration": "production"
},
"postinstall": {
"jest-extended/types": "copy node_modules/@types/jest-extended"
21 changes: 17 additions & 4 deletions scripts/build-pwa.js
Original file line number Diff line number Diff line change
@@ -10,10 +10,23 @@ if (configuration === 'true') {
}

if (!configuration) {
console.log('falling back to configuration "production"');
configuration = 'production';
configuration = process.env.npm_package_config_default_build_configuration || 'production';

console.log(`falling back to configuration "${configuration}"`);
console.log('you can run other configuration(s) with npm using the form "--configuration=<config1>,production"');
}

execSync('npm run ng -- build -c ' + configuration, { stdio: [0, 1, 2] });
execSync('npm run ng -- run intershop-pwa:server -c ' + configuration, { stdio: [0, 1, 2] });
const client = process.argv[2] !== 'server';
const server = process.argv[2] !== 'client';
const full = (client && server) || !(client && server);
const remainingArgs = process.argv.slice(full ? 2 : 3);

if (client) {
execSync(`npm run ng -- build -c ${configuration} ${remainingArgs.join(' ')}`, { stdio: 'inherit' });
}

if (server) {
execSync(`npm run ng -- run intershop-pwa:server -c ${configuration} ${remainingArgs.join(' ')}`, {
stdio: 'inherit',
});
}
26 changes: 26 additions & 0 deletions scripts/set-deploy-url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// tslint:disable: ish-ordered-imports ban-specific-imports no-console
import * as fs from 'fs';
import * as glob from 'glob';
import * as path from 'path';
import { setDeployUrlInFile } from '../src/ssr/deploy-url';

if (process.argv.length < 3) {
console.error('required argument deployUrl missing');
process.exit(1);
}

let deployUrl = process.argv[2];

if (!deployUrl.endsWith('/')) {
deployUrl += '/';
}

glob.sync('dist/browser/*.{js,css,html}').forEach(file => {
console.log(`setting deployUrl "${deployUrl}" in`, file);

const input = fs.readFileSync(file, { encoding: 'utf-8' });

const output = setDeployUrlInFile(deployUrl, path.basename(file), input);

fs.writeFileSync(file, output);
});
38 changes: 31 additions & 7 deletions server.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// tslint:disable: no-console ish-ordered-imports force-jsdoc-comments
// tslint:disable: no-console ish-ordered-imports force-jsdoc-comments ban-specific-imports
import 'zone.js/dist/zone-node';

import * as express from 'express';
import { join } from 'path';
import * as robots from 'express-robots-txt';
import * as fs from 'fs';
import * as proxy from 'express-http-proxy';
// tslint:disable-next-line: ban-specific-imports
import { AppServerModule, ICM_WEB_URL, HYBRID_MAPPING_TABLE, environment, APP_BASE_HREF } from './src/main.server';
import { ngExpressEngine } from '@nguniversal/express-engine';
import { getDeployURLFromEnv, setDeployUrlInFile } from './src/ssr/deploy-url';

const PORT = process.env.PORT || 4200;

const DEPLOY_URL = getDeployURLFromEnv();

const DIST_FOLDER = join(process.cwd(), 'dist');

// uncomment this block to prevent ssr issues with third-party libraries regarding window, document, HTMLElement and navigator
@@ -28,8 +30,8 @@ global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
// tslint:enable:no-string-literal
*/

// The Express app is exported so that it can be used by serverless Functions.
// not-dead-code
export function app() {
const logging = /on|1|true|yes/.test(process.env.LOGGING?.toLowerCase());

@@ -65,7 +67,7 @@ export function app() {
if (logging) {
server.use(
require('morgan')('tiny', {
skip: req => req.originalUrl.startsWith('/INTERSHOP/static'),
skip: (req: express.Request) => req.originalUrl.startsWith('/INTERSHOP/static'),
})
);
}
@@ -95,6 +97,18 @@ export function app() {
);
}

server.get(/\/.*\.(js|css)$/, (req, res) => {
const path = req.originalUrl.substring(1);
fs.readFile(join(DIST_FOLDER, 'browser', path), { encoding: 'utf-8' }, (err, data) => {
if (err) {
res.sendStatus(404);
} else {
res.set('Content-Type', (path.endsWith('css') ? 'text/css' : 'application/javascript') + '; charset=UTF-8');
res.send(setDeployUrlInFile(DEPLOY_URL, path, data));
}
});
});

// Serve static files from /browser
server.get(
'*.*',
@@ -107,14 +121,22 @@ export function app() {
// file should be re-checked more frequently -> 5m
res.set('Cache-Control', 'public, max-age=300');
}
// add cors headers for required resources
if (
DEPLOY_URL.startsWith('http') &&
['manifest.webmanifest', 'woff2', 'woff', 'json'].some(match => path.endsWith(match))
) {
res.set('access-control-allow-origin', '*');
}
},
})
);

const icmProxy = proxy(ICM_BASE_URL, {
// preserve original path
proxyReqPathResolver: req => req.originalUrl,
proxyReqOptDecorator: options => {
proxyReqPathResolver: (req: express.Request) => req.originalUrl,
// tslint:disable-next-line: no-any
proxyReqOptDecorator: (options: any) => {
if (process.env.TRUST_ICM) {
// https://github.com/villadora/express-http-proxy#q-how-to-ignore-self-signed-certificates-
options.rejectUnauthorized = false;
@@ -173,7 +195,9 @@ export function app() {
}
newHtml = newHtml.replace(/<base href="[^>]*>/, `<base href="${baseHref}" />`);

res.status(res.statusCode).send(newHtml || html);
newHtml = setDeployUrlInFile(DEPLOY_URL, req.originalUrl, newHtml);

res.status(res.statusCode).send(newHtml);
} else {
res.status(500).send(err.message);
}
Loading