build(docs-infra): simplify `ExampleZipper` by removing `PackageJsonCustomizer` (#38192)

Previously, `ExampleZipper` (the tool used for creating ZIP archives
from our docs examples) used the `PackageJsonCustomizer` to generate
`package.json` files for each example type. This had the following
drawbacks:
- The generated files had to be kept up-to-date with the corresponding
  boilerplate files in `aio/tools/examples/shared/boilerplate/` and
  there was no easy way to find out when the files got out-of-sync.
- The `PackageJsonCustomizer` logic was non-trivial and difficult to
  reason about.
- The same information was duplicated in the boilerplate files and the
  customizer configuration files.

This setup was useful when we used a single `package.json` file for all
docs examples. Now, however, each example type can have its own
boilerplate `package.json` file, including scripts and dependencies
relevant to the example type. Therefore, it is no longer necessary to
generate `package.json` files for ZIP archives.

This commit eliminates the drawbacks mentioned above and simplifies the
`ExampleZipper` tool by removing `PackageJsonCustomizer` and re-using
the boilerplate `package.json` files for ZIP archives.

The changes in this commit also fix some ZIP archives that were
previously broken (for example due to missing dependencies).

PR Close #38192
This commit is contained in:
George Kalpakas 2020-07-22 22:23:55 +03:00 committed by Misko Hevery
parent ee22aa592e
commit 88d4b269b5
22 changed files with 32 additions and 409 deletions

View File

@ -14,11 +14,3 @@ There, select all the packages that are updated on the new Angular release.
**2)** Changes to the tsconfig.json? There are several files in `/aio/tools/examples/shared/boilerplate/*/tsconfig.json` (based on the example type). **2)** Changes to the tsconfig.json? There are several files in `/aio/tools/examples/shared/boilerplate/*/tsconfig.json` (based on the example type).
---
> NOTE(gkalpak):
> There are some `package.json` files in `/aio/tools/examples/shared/boilerplate/*`.
> AFAICT, they are copied over to the examples (based on the example type), but they are neither
> used for installing dependencies (which come from `/aio/tools/examples/shared/package.json`) nor
> used in zips (since they are overwritten by `/aio/tools/example-zipper/customizer`).
> For all stackblitz live-examples, `/aio/tools/examples/shared/boilerplate/cli/package.json` seems
> to be used.

View File

@ -23,33 +23,18 @@ to flag an example as something to stackblitz or zip. For example:
The zipper will use this information for creating new zips. The zipper will use this information for creating new zips.
## Three kinds of examples ## Two kinds of examples
The majority of examples in AIO use `CLI`, with some additionally using `Webpack` and upgrade usiing `SystemJS`. This There are mainly two kinds of AIO docs examples: The ones based on the Angular CLI and the ones based on SystemJS.
tool is able to differentiate between them. The majority of the examples are CLI-based with only some of the `ngUpgrade` examples using SystemJS.
The boilerplate uses a `package.json` that contains packages and scripts to run any kind of example. Some of the CLI-based examples require small tweaks to the default layout/configuration (for example, to add support for Angular elements, i18n, universal, etc.).
Using that `package.json` in the zips would confuse the users. These example types have separate boilerplate directories with the files that are different from the default `cli` boilerplate.
Thanks to the `package.json` customizer, we can create a new `package.json` on the fly that would There are appropriate `package.json` files for each type of example in the boilerplate directories.
only contain the packages and scripts needed to run that example. If there is no special `package.json` file for an example type, the one from the `cli` boilerplate directory will be used instead.
The `exampleZipper.js` won't include any `System.js` related files for `CLI` or `Webpack` projects. The `exampleZipper.js` won't include any `System.js` related files for CLI-based projects.
### The package.json customizer
Given a `type`, this tool will populate a `package.json` file customized for that type.
Here you find a:
* **base.json** - All the common scripts and packages
* **cli.json** - Extra scripts and packages for the CLI
* **universal.json** - Extra scripts and packages for universal
* **i18n.json** - Extra scripts and packages for i18n
* **systemjs.json** - All the System.js related packages but it also contains the remainder scripts
that are not in the other files.
The tool will also give some standard names to the scripts.
## The zipper.json ## The zipper.json

View File

@ -1,34 +0,0 @@
{
"scripts": [
{ "name": "lint" }
],
"dependencies": [
"@angular/animations",
"@angular/common",
"@angular/compiler",
"@angular/core",
"@angular/forms",
"@angular/platform-browser",
"@angular/platform-browser-dynamic",
"@angular/router",
"@angular/upgrade",
"angular-in-memory-web-api",
"rxjs",
"zone.js"
],
"devDependencies": [
"@angular/compiler-cli",
"@types/jasmine",
"@types/node",
"jasmine-core",
"karma",
"karma-chrome-launcher",
"karma-cli",
"karma-jasmine",
"karma-jasmine-html-reporter",
"lodash",
"protractor",
"tslint",
"typescript"
]
}

View File

@ -1,23 +0,0 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" }
],
"dependencies": [
"angular",
"angular-route"
],
"devDependencies": [
"@angular/cli",
"@types/angular",
"@types/angular-route",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -1,19 +0,0 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" }
],
"dependencies": [],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -1,22 +0,0 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" }
],
"dependencies": [
"@angular/elements",
"@webcomponents/custom-elements"
],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -1,19 +0,0 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" }
],
"dependencies": [],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -1,21 +0,0 @@
{
"scripts": [
{ "name": "start", "command": "ng serve" },
{ "name": "start:fr", "command": "ng serve --configuration=fr" },
{ "name": "build", "command": "ng build" },
{ "name": "build:fr", "command": "ng build --configuration=production-fr" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" },
{ "name": "extract", "command": "ng xi18n --output-path=locale" }
],
"dependencies": [],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -1,19 +0,0 @@
{
"name": "angular-io-example",
"version": "1.0.0",
"private": true,
"description": "Example project from an angular.io guide.",
"scripts": {
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
},
"devDependencies": {
},
"repository": {}
}

View File

@ -1,63 +0,0 @@
'use strict';
const path = require('canonical-path');
const fs = require('fs');
const examplesPath = path.resolve(__dirname, '../../../examples');
const packageFolder = path.resolve(__dirname);
class PackageJsonCustomizer {
constructor() {
this.dependenciesPackageJson = this.readJson(path.join(examplesPath, '/shared/package.json'));
this.scriptsPackageJson = this.readJson(path.join(examplesPath, '/shared/boilerplate/systemjs/package.json'));
this.basePackageJson = this.readJson(`${packageFolder}/base.json`);
this.templatePackageJson = this.readJson(`${packageFolder}/package.json`, false);
}
generate(type = 'systemjs') {
let packageJson = JSON.parse(this.templatePackageJson);
let rules = require(`${packageFolder}/${type}.json`);
this._mergeJSON(rules, this.basePackageJson);
rules.scripts.forEach((r) => {
const scriptName = r.name;
const script = this.scriptsPackageJson.scripts[scriptName];
const finalName = r.rename ? r.rename : r.name;
const finalScript = r.command ? r.command : script;
packageJson.scripts[finalName] = finalScript;
});
rules.dependencies.forEach((name) => {
const version = this.dependenciesPackageJson.dependencies[name];
packageJson.dependencies[name] = version;
});
rules.devDependencies.forEach((name) => {
const version = this.dependenciesPackageJson.devDependencies[name];
packageJson.devDependencies[name] = version;
});
return JSON.stringify(packageJson, null, 2);
}
_mergeJSON(json1, json2) {
var result = json1;
for (var prop in json2)
{
if (json2.hasOwnProperty(prop))
{
result[prop] = (result[prop].concat(json2[prop])).sort();
}
}
return result;
}
readJson(jsonFile, parse = true) {
const contents = fs.readFileSync(jsonFile, 'utf8');
return parse ? JSON.parse(contents) : contents;
}
}
module.exports = PackageJsonCustomizer;

View File

@ -1,24 +0,0 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "build:lib", "command": "ng build my-lib" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" }
],
"dependencies": [],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular-devkit/build-ng-packagr",
"@angular/cli",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ng-packagr",
"tsickle",
"tslib",
"ts-node"
]
}

View File

@ -1,60 +0,0 @@
{
"scripts": [
{ "name": "build" },
{ "name": "build:watch" },
{ "name": "serve" },
{ "name": "prestart" },
{ "name": "start" },
{ "name": "pretest" },
{ "name": "test" },
{ "name": "pretest:once" },
{ "name": "test:once" },
{ "name": "build:upgrade" },
{ "name": "serve:upgrade" },
{ "name": "build:aot" },
{ "name": "serve:aot" },
{ "name": "copy-dist-files" }
],
"dependencies": [
"@angular/animations",
"@angular/common",
"@angular/compiler",
"@angular/core",
"@angular/forms",
"@angular/platform-browser",
"@angular/platform-browser-dynamic",
"@angular/router",
"@angular/upgrade",
"core-js",
"rxjs",
"systemjs",
"tslib",
"zone.js"
],
"devDependencies": [
"@angular/compiler-cli",
"@types/angular",
"@types/angular-animate",
"@types/angular-mocks",
"@types/angular-resource",
"@types/angular-route",
"@types/jasmine",
"@types/jasminewd2",
"@types/node",
"concurrently",
"http-server",
"jasmine-core",
"karma",
"karma-chrome-launcher",
"karma-jasmine",
"karma-jasmine-html-reporter",
"lite-server",
"protractor",
"rollup",
"rollup-plugin-commonjs",
"rollup-plugin-node-resolve",
"rollup-plugin-uglify",
"tslint",
"typescript"
]
}

View File

@ -1,20 +0,0 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" }
],
"dependencies": [],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@types/jasminewd2",
"jasmine-marbles",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -1,29 +0,0 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" },
{ "name": "dev:ssr", "command": "ng run angular.io-example:serve-ssr" },
{ "name": "build:ssr", "command": "ng build --prod && ng run angular.io-example:server:production" },
{ "name": "serve:ssr", "command": "node dist/server/main.js" },
{ "name": "prerender", "command": "ng run angular.io-example:prerender" }
],
"dependencies": [
"@angular/platform-server",
"@nguniversal/express-engine",
"express"
],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@nguniversal/builders",
"@types/express",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -6,7 +6,6 @@ const archiver = require('archiver');
const fs = require('fs-extra'); const fs = require('fs-extra');
const globby = require('globby'); const globby = require('globby');
const PackageJsonCustomizer = require('./customizer/package-json/packageJsonCustomizer');
const regionExtractor = require('../transforms/examples-package/services/region-parser'); const regionExtractor = require('../transforms/examples-package/services/region-parser');
const EXAMPLE_CONFIG_NAME = 'example-config.json'; const EXAMPLE_CONFIG_NAME = 'example-config.json';
@ -17,7 +16,6 @@ class ExampleZipper {
this.examplesSystemjsConfig = path.join(__dirname, '../examples/shared/boilerplate/systemjs/src/systemjs.config.js'); this.examplesSystemjsConfig = path.join(__dirname, '../examples/shared/boilerplate/systemjs/src/systemjs.config.js');
this.examplesSystemjsLoaderConfig = path.join(__dirname, '../examples/shared/boilerplate/systemjs/src/systemjs-angular-loader.js'); this.examplesSystemjsLoaderConfig = path.join(__dirname, '../examples/shared/boilerplate/systemjs/src/systemjs-angular-loader.js');
this.exampleTsconfig = path.join(__dirname, '../examples/shared/boilerplate/systemjs/src/tsconfig.json'); this.exampleTsconfig = path.join(__dirname, '../examples/shared/boilerplate/systemjs/src/tsconfig.json');
this.customizer = new PackageJsonCustomizer();
let gpathStackblitz = path.join(sourceDirName, '**/*stackblitz.json'); let gpathStackblitz = path.join(sourceDirName, '**/*stackblitz.json');
let gpathZipper = path.join(sourceDirName, '**/zipper.json'); let gpathZipper = path.join(sourceDirName, '**/zipper.json');
@ -91,6 +89,7 @@ class ExampleZipper {
'bs-config.json', 'bs-config.json',
'karma.conf.js', 'karma.conf.js',
'karma-test-shim.js', 'karma-test-shim.js',
'package.json',
'tsconfig.*', 'tsconfig.*',
'tslint.*', 'tslint.*',
'e2e/protractor.conf.js', 'e2e/protractor.conf.js',
@ -100,8 +99,6 @@ class ExampleZipper {
'src/test.ts', 'src/test.ts',
'src/environments/**/*', 'src/environments/**/*',
'src/testing/**/*', 'src/testing/**/*',
// Only ignore root package.json
'!package.json'
]; ];
var alwaysExcludes = [ var alwaysExcludes = [
'!**/bs-config.e2e.json', '!**/bs-config.e2e.json',
@ -167,8 +164,6 @@ class ExampleZipper {
zip.append(output, { name: relativePath } ); zip.append(output, { name: relativePath } );
}); });
// we need the package.json from _examples root, not the _boilerplate one
zip.append(this.customizer.generate(exampleType), { name: 'package.json' });
// also a systemjs config // also a systemjs config
if (exampleType === 'systemjs') { if (exampleType === 'systemjs') {
zip.append(fs.readFileSync(this.examplesSystemjsConfig, 'utf8'), { name: 'src/systemjs.config.js' }); zip.append(fs.readFileSync(this.examplesSystemjsConfig, 'utf8'), { name: 'src/systemjs.config.js' });

View File

@ -1,6 +1,7 @@
{ {
"name": "angular.io-example", "name": "angular.io-example",
"version": "0.0.0", "version": "0.0.0",
"description": "Example project from an angular.io guide.",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

View File

@ -1,6 +1,7 @@
{ {
"name": "angular.io-example", "name": "angular.io-example",
"version": "0.0.0", "version": "0.0.0",
"description": "Example project from an angular.io guide.",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

View File

@ -1,6 +1,7 @@
{ {
"name": "angular.io-example", "name": "angular.io-example",
"version": "0.0.0", "version": "0.0.0",
"description": "Example project from an angular.io guide.",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

View File

@ -1,6 +1,7 @@
{ {
"name": "angular.io-example", "name": "angular.io-example",
"version": "0.0.0", "version": "0.0.0",
"description": "Example project from an angular.io guide.",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

View File

@ -1,6 +1,7 @@
{ {
"name": "angular.io-example", "name": "angular.io-example",
"version": "0.0.0", "version": "0.0.0",
"description": "Example project from an angular.io guide.",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",

View File

@ -1,8 +1,8 @@
{ {
"name": "angular-examples", "name": "angular.io-example",
"version": "1.0.0", "version": "0.0.0",
"private": true, "description": "Example project from an angular.io guide.",
"description": "Example package.json, only contains needed scripts for examples. See _examples/package.json for master package.json.", "license": "MIT",
"scripts": { "scripts": {
"build": "tsc -p src/", "build": "tsc -p src/",
"build:watch": "tsc -p src/ -w", "build:watch": "tsc -p src/ -w",
@ -25,26 +25,25 @@
"serve:aot": "lite-server -c bs-config.aot.json", "serve:aot": "lite-server -c bs-config.aot.json",
"copy-dist-files": "node ./copy-dist-files.js" "copy-dist-files": "node ./copy-dist-files.js"
}, },
"keywords": [], "private": true,
"author": "",
"license": "MIT",
"dependencies": { "dependencies": {
"@angular/animations": "~9.0.3", "@angular/animations": "~9.1.4",
"@angular/common": "~9.0.3", "@angular/common": "~9.1.4",
"@angular/compiler": "~9.0.3", "@angular/compiler": "~9.1.4",
"@angular/core": "~9.0.3", "@angular/core": "~9.1.4",
"@angular/forms": "~9.0.3", "@angular/forms": "~9.1.4",
"@angular/platform-browser": "~9.0.3", "@angular/platform-browser": "~9.1.4",
"@angular/platform-browser-dynamic": "~9.0.3", "@angular/platform-browser-dynamic": "~9.1.4",
"@angular/router": "~9.0.3", "@angular/router": "~9.1.4",
"@angular/upgrade": "~9.0.3", "@angular/upgrade": "~9.1.4",
"core-js": "^2.5.4", "core-js": "^2.5.4",
"rxjs": "~6.5.4", "rxjs": "~6.5.4",
"tslib": "^1.10.0", "tslib": "^1.10.0",
"zone.js": "~0.10.3" "zone.js": "~0.10.3"
}, },
"devDependencies": { "devDependencies": {
"@angular/compiler-cli": "~9.0.3", "@angular/compiler-cli": "~9.1.4",
"@angular/language-service": "~9.1.4",
"@types/angular": "1.6.47", "@types/angular": "1.6.47",
"@types/angular-animate": "1.5.10", "@types/angular-animate": "1.5.10",
"@types/angular-mocks": "1.6.0", "@types/angular-mocks": "1.6.0",
@ -68,6 +67,5 @@
"rollup-plugin-terser": "^5.3.0", "rollup-plugin-terser": "^5.3.0",
"tslint": "~5.18.0", "tslint": "~5.18.0",
"typescript": "~3.7.5" "typescript": "~3.7.5"
}, }
"repository": {}
} }

View File

@ -1,6 +1,7 @@
{ {
"name": "angular.io-example", "name": "angular.io-example",
"version": "0.0.0", "version": "0.0.0",
"description": "Example project from an angular.io guide.",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",