build(docs-infra): add a tool to create new examples (#39283)
This tool can be run from anywhere in the aio folder as: ```sh yarn create-example <example-name> ``` It will create some basic scaffold files to get the example started. After creation the developer should then use `yarn boilerplate:add` or similar to ensure that the example can be run and tested. You can optionally provide an absolute path to a pre-existing CLI project and it will copy over appropriate files (ignoring boilerplate) to the newly created example. ```sh yarn create-example <example-name> /path/to/other/cli/project ``` Fixes #39275 PR Close #39283
This commit is contained in:
parent
81aa119739
commit
34b74cecb6
|
@ -36,8 +36,9 @@ Here are the most important tasks you might need to use:
|
|||
|
||||
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
|
||||
* `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
|
||||
|
||||
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
|
||||
* `yarn create-example` - create a new example directory containing initial source files.
|
||||
|
||||
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
|
||||
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
**/karma-test-shim.js
|
||||
**/browser-test-shim.js
|
||||
**/node_modules
|
||||
**/yarn.lock
|
||||
**/package-lock.json
|
||||
|
||||
# built files
|
||||
*.map
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
"boilerplate:test": "node tools/examples/test.js",
|
||||
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
|
||||
"generate-zips": "node ./tools/example-zipper/generateZips",
|
||||
"create-example": "node ./tools/examples/create-example.js",
|
||||
"build-404-page": "node scripts/build-404-page",
|
||||
"update-webdriver": "node ../scripts/webdriver-manager-update.js",
|
||||
"~~audit-web-app": "node scripts/audit-web-app",
|
||||
|
@ -172,6 +173,6 @@
|
|||
"unist-util-visit-parents": "^1.1.1",
|
||||
"watchr": "^3.0.1",
|
||||
"xregexp": "^4.0.0",
|
||||
"yargs": "^7.0.2"
|
||||
"yargs": "^16.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,11 +29,12 @@ sub-folder. Also there are a number of common boilerplate files that are needed
|
|||
example's project. We maintain these common boilerplate files centrally to reduce the amount of effort
|
||||
if one of them needs to change.
|
||||
|
||||
This `examples` tool folder contains two utilities:
|
||||
This `examples` tool folder contains three utilities:
|
||||
|
||||
* example-boilerplate.js - install/remove the npm dependencies and boilerplate files into/from each of the
|
||||
examples' subfolders.
|
||||
* run-example-e2e.js - run the e2e tests for one or more examples
|
||||
* create-example.js - create a new example from the `example-scaffold/` directory or by importing files from a CLI project.
|
||||
|
||||
See the [README.md](examples/README.md) for more details.
|
||||
|
||||
|
|
|
@ -150,6 +150,14 @@ See [aio/README.md](../../README.md#developer-tasks) for the available command-l
|
|||
|
||||
Running the script will create an `aio/protractor-results.txt` file with the results of the tests.
|
||||
|
||||
### `create-example.js`
|
||||
|
||||
The [create-example.js](./create-example.js) script creates a new example under the `aio/content/examples` directory.
|
||||
|
||||
You must provide a new name for the example.
|
||||
By default the script will place basic scaffold files into the new example (from [shared/example-scaffold](./shared/example-scaffold)).
|
||||
But you can also specify the path to a separate CLI project, from which the script will copy files that would not be considered "boilerplate".
|
||||
See the [Boilerplate overview](#boilerplate-overview) for more information.
|
||||
|
||||
### Updating example dependencies
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@ Follow these steps to update the examples to the latest versions of Angular (and
|
|||
> NOTE:
|
||||
> The [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repo can be a useful resource for discovering what dependency versions are used for a basic CLI app at a specific CLI version.
|
||||
|
||||
- In the [shared/](./shared) folder, run `yarn` to update the dependencies in the [shared/node_modules/](./shared/node_modules) folder and the [shared/yarn.lock](./shared/yarn.lock) file.
|
||||
- In the [shared/](./shared) directory, run `yarn` to update the dependencies in the [shared/node_modules/](./shared/node_modules) directory and the [shared/yarn.lock](./shared/yarn.lock) file.
|
||||
|
||||
- In the [shared/](./shared) folder, run `yarn sync-deps` to update the dependency versions of the `package.json` files in each sub-folder of [shared/boilerplate/](./shared/boilerplate) to match the ones in [shared/package.json](./shared/package.json).
|
||||
- In the [shared/](./shared) directory, run `yarn sync-deps` to update the dependency versions of the `package.json` files in each sub-folder of [shared/boilerplate/](./shared/boilerplate) to match the ones in [shared/package.json](./shared/package.json).
|
||||
|
||||
- Follow the steps in the following section to update the rest of the boilerplate files.
|
||||
|
||||
|
@ -24,7 +24,7 @@ Any necessary changes to boilerplate files will be done automatically through mi
|
|||
> You have to make these changes (if any) manually.
|
||||
> Again, the [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repo can be a useful resource for discovering changes between versions.
|
||||
|
||||
- In the [shared/boilerplate/cli/](./shared/boilerplate/cli) folder, run the following commands to migrate the the project to the current versions of Angular CLI and the Angular framework (updated in previous steps):
|
||||
- In the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory, run the following commands to migrate the the project to the current versions of Angular CLI and the Angular framework (updated in previous steps):
|
||||
```sh
|
||||
# Ensure dependencies are installed.
|
||||
yarn install
|
||||
|
@ -38,8 +38,11 @@ Any necessary changes to boilerplate files will be done automatically through mi
|
|||
> In order for `ng update` to work, there must be a `node_modules/` directory with installed dependencies inside the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory.
|
||||
> This `node_modules/` directory is only needed during the update operation and is otherwise ignored (both by git and by the [example-boilerplate.js](./example-boilerplate.js) script) by means of the [shared/boilerplate/.gitignore](./shared/boilerplate/.gitignore) file.
|
||||
|
||||
- The previous command made any necessary changes to boilerplate files inside the `cli/` folder, but the same changes need to be applied to the other CLI-based boilerplate folders.
|
||||
Inspect the changes in `cli/` and manually apply the necessary ones to other CLI-based boilerplate folders.
|
||||
- The previous command made any necessary changes to boilerplate files inside the `cli/` directory, but the same changes need to be applied to the other CLI-based boilerplate directories.
|
||||
Inspect the changes in `cli/` and manually apply the necessary ones to other CLI-based boilerplate directories.
|
||||
|
||||
- Also ensure that any relevant changes in the [shared/boilerplate/cli/](./shared/boilerplate/cli) directory are copied to the [shared/example-scaffold/](./shared/example-scaffold) directory, which is used when creating new examples (via `yarn create-example ...`).
|
||||
Only files that would not be considered boilerplate should be added to the `example-scaffold/` directory.
|
||||
|
||||
- Ensure any changes to [cli/tslint.json](./shared/boilerplate/cli/tslint.json) are ported over to [systemjs/tslint.json](./shared/boilerplate/systemjs/tslint.json) and also [aio/content/examples/tslint.json](../../content/examples/tslint.json).
|
||||
This last part is important, since this file is used to lint example code on CI.
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
const path = require('canonical-path');
|
||||
|
||||
exports.EXAMPLES_BASE_PATH = path.resolve(__dirname, '../../content/examples');
|
||||
exports.EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
||||
exports.SHARED_PATH = path.resolve(__dirname, 'shared');
|
||||
exports.STACKBLITZ_CONFIG_FILENAME = 'stackblitz.json';
|
|
@ -0,0 +1,140 @@
|
|||
const fs = require('fs-extra');
|
||||
const glob = require('glob');
|
||||
const ignore = require('ignore');
|
||||
const path = require('canonical-path');
|
||||
const shelljs = require('shelljs');
|
||||
const yargs = require('yargs');
|
||||
|
||||
const {EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, SHARED_PATH, STACKBLITZ_CONFIG_FILENAME} =
|
||||
require('./constants');
|
||||
const BASIC_SOURCE_PATH = path.resolve(SHARED_PATH, 'example-scaffold');
|
||||
|
||||
shelljs.set('-e');
|
||||
|
||||
if (require.main === module) {
|
||||
const options =
|
||||
yargs(process.argv.slice(2))
|
||||
.command(
|
||||
'$0 <name> [source]',
|
||||
[
|
||||
'Create a new <name> example.',
|
||||
'',
|
||||
'If [source] is provided then the relevant files from the CLI project at that path are copied into the example.',
|
||||
].join('\n'))
|
||||
.strict()
|
||||
.version(false)
|
||||
.argv;
|
||||
|
||||
const exampleName = options.name;
|
||||
const examplePath = path.resolve(EXAMPLES_BASE_PATH, exampleName);
|
||||
|
||||
console.log('Creating new example at', examplePath);
|
||||
createEmptyExample(exampleName, examplePath);
|
||||
|
||||
const sourcePath =
|
||||
options.source !== undefined ? path.resolve(options.source) : BASIC_SOURCE_PATH;
|
||||
console.log('Copying files from', sourcePath);
|
||||
copyExampleFiles(sourcePath, examplePath, exampleName);
|
||||
|
||||
console.log(`The new "${exampleName}" example has been created.`);
|
||||
console.log('Now run "yarn boilerplate:add" to set it up for development.');
|
||||
console.log(
|
||||
'You can find more info on working with docs examples in aio/tools/examples/README.md.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the directory and marker files for the new example.
|
||||
*/
|
||||
function createEmptyExample(exampleName, examplePath) {
|
||||
ensureExamplePath(examplePath);
|
||||
writeExampleConfigFile(examplePath);
|
||||
writeStackBlitzFile(exampleName, examplePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the new example directory exists.
|
||||
*/
|
||||
function ensureExamplePath(examplePath) {
|
||||
if (fs.existsSync(examplePath)) {
|
||||
throw new Error(
|
||||
`Unable to create example. The path to the new example already exists: ${examplePath}`);
|
||||
}
|
||||
fs.ensureDirSync(examplePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the `example-config.json` file to the new example.
|
||||
*/
|
||||
function writeExampleConfigFile(examplePath) {
|
||||
fs.writeFileSync(path.resolve(examplePath, EXAMPLE_CONFIG_FILENAME), '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the `stackblitz.json` file into the new example.
|
||||
*/
|
||||
function writeStackBlitzFile(exampleName, examplePath) {
|
||||
const config = {
|
||||
description: titleize(exampleName),
|
||||
files: ['!**/*.d.ts', '!**/*.js', '!**/*.[1,2].*'],
|
||||
tags: [exampleName.split('-')]
|
||||
};
|
||||
fs.writeFileSync(
|
||||
path.resolve(examplePath, STACKBLITZ_CONFIG_FILENAME),
|
||||
JSON.stringify(config, null, 2) + '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy all the files from the `sourcePath`, which are not ignored by the `.gitignore` file in the
|
||||
* `EXAMPLES_BASE_PATH`, to the `examplePath`.
|
||||
*/
|
||||
function copyExampleFiles(sourcePath, examplePath, exampleName) {
|
||||
const gitIgnoreSource = getGitIgnore(sourcePath);
|
||||
const gitIgnoreExamples = getGitIgnore(EXAMPLES_BASE_PATH);
|
||||
|
||||
// Grab the files in the source folder and filter them based on the gitignore rules.
|
||||
const sourceFiles =
|
||||
glob.sync('**/*', {
|
||||
cwd: sourcePath,
|
||||
dot: true,
|
||||
ignore: ['**/node_modules/**', '.git/**', '.gitignore'],
|
||||
mark: true
|
||||
})
|
||||
// Filter out the directories, leaving only files
|
||||
.filter(filePath => !/\/$/.test(filePath))
|
||||
// Filter out files that match the source directory .gitignore rules
|
||||
.filter(filePath => !gitIgnoreSource.ignores(filePath))
|
||||
// Filter out files that match the examples directory .gitignore rules
|
||||
.filter(filePath => !gitIgnoreExamples.ignores(path.join(exampleName, filePath)));
|
||||
|
||||
for (const sourceFile of sourceFiles) {
|
||||
console.log(' - ', sourceFile);
|
||||
const destPath = path.resolve(examplePath, sourceFile)
|
||||
fs.ensureDirSync(path.dirname(destPath));
|
||||
fs.copySync(path.resolve(sourcePath, sourceFile), destPath);
|
||||
}
|
||||
}
|
||||
|
||||
function getGitIgnore(directory) {
|
||||
const gitIgnoreMatcher = ignore();
|
||||
const gitignoreFilePath = path.resolve(directory, '.gitignore');
|
||||
if (fs.existsSync(gitignoreFilePath)) {
|
||||
const gitignoreFile = fs.readFileSync(gitignoreFilePath, 'utf8');
|
||||
gitIgnoreMatcher.add(gitignoreFile);
|
||||
}
|
||||
return gitIgnoreMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a kebab-case string to space separated Title Case string.
|
||||
*/
|
||||
function titleize(input) {
|
||||
return input.replace(
|
||||
/(-|^)(.)/g, (_, pre, char) => `${pre === '-' ? ' ' : ''}${char.toUpperCase()}`);
|
||||
}
|
||||
|
||||
exports.createEmptyExample = createEmptyExample;
|
||||
exports.ensureExamplePath = ensureExamplePath;
|
||||
exports.writeExampleConfigFile = writeExampleConfigFile;
|
||||
exports.writeStackBlitzFile = writeStackBlitzFile;
|
||||
exports.copyExampleFiles = copyExampleFiles;
|
||||
exports.titleize = titleize;
|
|
@ -0,0 +1,130 @@
|
|||
const path = require('canonical-path');
|
||||
const fs = require('fs-extra');
|
||||
const {glob} = require('glob');
|
||||
|
||||
const {EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, SHARED_PATH, STACKBLITZ_CONFIG_FILENAME} =
|
||||
require('./constants');
|
||||
|
||||
const {
|
||||
copyExampleFiles,
|
||||
createEmptyExample,
|
||||
ensureExamplePath,
|
||||
titleize,
|
||||
writeExampleConfigFile,
|
||||
writeStackBlitzFile
|
||||
} = require('./create-example');
|
||||
|
||||
describe('create-example tool', () => {
|
||||
describe('createEmptyExample', () => {
|
||||
it('should create an empty example with marker files', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(false);
|
||||
spyOn(fs, 'ensureDirSync');
|
||||
const writeFileSpy = spyOn(fs, 'writeFileSync');
|
||||
|
||||
createEmptyExample('foo-bar', '/path/to/foo-bar');
|
||||
expect(writeFileSpy).toHaveBeenCalledTimes(2);
|
||||
expect(writeFileSpy)
|
||||
.toHaveBeenCalledWith(`/path/to/foo-bar/${EXAMPLE_CONFIG_FILENAME}`, jasmine.any(String));
|
||||
expect(writeFileSpy)
|
||||
.toHaveBeenCalledWith(
|
||||
`/path/to/foo-bar/${STACKBLITZ_CONFIG_FILENAME}`, jasmine.any(String));
|
||||
});
|
||||
});
|
||||
|
||||
describe('ensureExamplePath', () => {
|
||||
it('should error if the path already exists', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
expect(() => ensureExamplePath('foo/bar'))
|
||||
.toThrowError(
|
||||
`Unable to create example. The path to the new example already exists: foo/bar`);
|
||||
});
|
||||
|
||||
it('should create the directory on disk', () => {
|
||||
spyOn(fs, 'existsSync').and.returnValue(false);
|
||||
const spy = spyOn(fs, 'ensureDirSync');
|
||||
ensureExamplePath('foo/bar');
|
||||
expect(spy).toHaveBeenCalledWith('foo/bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeExampleConfigFile', () => {
|
||||
it('should write a JSON file to disk', () => {
|
||||
const spy = spyOn(fs, 'writeFileSync');
|
||||
writeExampleConfigFile('/foo/bar');
|
||||
expect(spy).toHaveBeenCalledWith(`/foo/bar/${EXAMPLE_CONFIG_FILENAME}`, '');
|
||||
});
|
||||
});
|
||||
|
||||
describe('writeStackBlitzFile', () => {
|
||||
it('should write a JSON file to disk', () => {
|
||||
const spy = spyOn(fs, 'writeFileSync');
|
||||
writeStackBlitzFile('bar-bar', '/foo/bar-bar');
|
||||
expect(spy).toHaveBeenCalledWith(`/foo/bar-bar/${STACKBLITZ_CONFIG_FILENAME}`, [
|
||||
'{',
|
||||
' "description": "Bar Bar",',
|
||||
' "files": [',
|
||||
' "!**/*.d.ts",',
|
||||
' "!**/*.js",',
|
||||
' "!**/*.[1,2].*"',
|
||||
' ],',
|
||||
' "tags": [',
|
||||
' [',
|
||||
' "bar",',
|
||||
' "bar"',
|
||||
' ]',
|
||||
' ]',
|
||||
'}',
|
||||
'',
|
||||
].join('\n'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('copyExampleFiles', () => {
|
||||
it('should copy over files that are not ignored by git', () => {
|
||||
const examplesGitIgnorePath = path.resolve(EXAMPLES_BASE_PATH, '.gitignore');
|
||||
const sourceGitIgnorePath = path.resolve('/source/path', '.gitignore');
|
||||
|
||||
spyOn(console, 'log');
|
||||
spyOn(fs, 'existsSync').and.returnValue(true);
|
||||
const readFileSyncSpy = spyOn(fs, 'readFileSync').and.callFake(p => {
|
||||
switch (p) {
|
||||
case examplesGitIgnorePath:
|
||||
return '**/a/b/**';
|
||||
case sourceGitIgnorePath:
|
||||
return '**/*.bad';
|
||||
default:
|
||||
throw new Error('Unexpected path');
|
||||
}
|
||||
});
|
||||
spyOn(glob, 'sync').and.returnValue([
|
||||
'a/', 'a/b/', 'a/c', 'x.ts', 'x.bad', 'a/b/y.ts', 'a/b/y.bad'
|
||||
]);
|
||||
const ensureDirSyncSpy = spyOn(fs, 'ensureDirSync');
|
||||
const copySyncSpy = spyOn(fs, 'copySync');
|
||||
|
||||
copyExampleFiles('/source/path', '/path/to/test-example', 'test-example');
|
||||
|
||||
expect(readFileSyncSpy).toHaveBeenCalledWith(examplesGitIgnorePath, 'utf8');
|
||||
expect(readFileSyncSpy).toHaveBeenCalledWith(sourceGitIgnorePath, 'utf8');
|
||||
|
||||
expect(ensureDirSyncSpy.calls.allArgs()).toEqual([
|
||||
['/path/to/test-example/a'],
|
||||
['/path/to/test-example'],
|
||||
]);
|
||||
|
||||
expect(copySyncSpy.calls.allArgs()).toEqual([
|
||||
['/source/path/a/c', '/path/to/test-example/a/c'],
|
||||
['/source/path/x.ts', '/path/to/test-example/x.ts'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('titleize', () => {
|
||||
it('should convert a kebab-case string to title-case', () => {
|
||||
expect(titleize('abc')).toEqual('Abc');
|
||||
expect(titleize('abc-def')).toEqual('Abc Def');
|
||||
expect(titleize('123')).toEqual('123');
|
||||
expect(titleize('abc---def')).toEqual('Abc - Def');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,8 +4,8 @@ const ignore = require('ignore');
|
|||
const path = require('canonical-path');
|
||||
const shelljs = require('shelljs');
|
||||
const yargs = require('yargs');
|
||||
const {EXAMPLES_BASE_PATH, EXAMPLE_CONFIG_FILENAME, SHARED_PATH} = require('./constants');
|
||||
|
||||
const SHARED_PATH = path.resolve(__dirname, 'shared');
|
||||
const SHARED_NODE_MODULES_PATH = path.resolve(SHARED_PATH, 'node_modules');
|
||||
|
||||
const BOILERPLATE_BASE_PATH = path.resolve(SHARED_PATH, 'boilerplate');
|
||||
|
@ -13,9 +13,6 @@ const BOILERPLATE_CLI_PATH = path.resolve(BOILERPLATE_BASE_PATH, 'cli');
|
|||
const BOILERPLATE_COMMON_PATH = path.resolve(BOILERPLATE_BASE_PATH, 'common');
|
||||
const BOILERPLATE_VIEWENGINE_PATH = path.resolve(BOILERPLATE_BASE_PATH, 'viewengine');
|
||||
|
||||
const EXAMPLES_BASE_PATH = path.resolve(__dirname, '../../content/examples');
|
||||
const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
||||
|
||||
class ExampleBoilerPlate {
|
||||
/**
|
||||
* Add boilerplate files to all the examples
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { AppPage } from './app.po';
|
||||
import { browser, logging } from 'protractor';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
// Add your e2e tests here
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
<h1>Replace the src folder in this {{title}} with yours.</h1>
|
|
@ -0,0 +1,20 @@
|
|||
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.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
// Add your unit tests here
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css'],
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'example';
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
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 { }
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Ponyracer</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>
|
|
@ -0,0 +1,12 @@
|
|||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
155
aio/yarn.lock
155
aio/yarn.lock
|
@ -3075,11 +3075,6 @@ camelcase@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
|
||||
integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
|
||||
|
||||
camelcase@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
|
||||
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
|
||||
|
||||
camelcase@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||
|
@ -3459,7 +3454,7 @@ cli-width@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
||||
|
||||
cliui@^3.0.3, cliui@^3.2.0:
|
||||
cliui@^3.0.3:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
|
||||
integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
|
||||
|
@ -3495,6 +3490,15 @@ cliui@^6.0.0:
|
|||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^6.2.0"
|
||||
|
||||
cliui@^7.0.2:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.2.tgz#e3a412e1d5ec0ccbe50d1b4120fc8164e97881f4"
|
||||
integrity sha512-lhpKkuUj67j5JgZIPZxLe7nSa4MQoojzRVWQyzMqBp2hBg6gwRjUDAwC1YDeBaC3APDBKNnjWbv2mlDF4XgOSA==
|
||||
dependencies:
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
clone@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
|
@ -4905,7 +4909,7 @@ errno@^0.1.1, errno@^0.1.3, errno@~0.1.7:
|
|||
dependencies:
|
||||
prr "~1.0.1"
|
||||
|
||||
error-ex@^1.2.0, error-ex@^1.3.1:
|
||||
error-ex@^1.3.1:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
|
||||
|
@ -5017,6 +5021,11 @@ es6-weak-map@^2.0.1, es6-weak-map@^2.0.2:
|
|||
es6-iterator "^2.0.3"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
|
@ -5583,14 +5592,6 @@ find-free-port@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/find-free-port/-/find-free-port-2.0.0.tgz#4b22e5f6579eb1a38c41ac6bcb3efed1b6da9b1b"
|
||||
integrity sha1-SyLl9leesaOMQaxryz7+0bbamxs=
|
||||
|
||||
find-up@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
|
||||
integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=
|
||||
dependencies:
|
||||
path-exists "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
find-up@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
|
||||
|
@ -5925,7 +5926,7 @@ get-caller-file@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a"
|
||||
integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==
|
||||
|
||||
get-caller-file@^2.0.1:
|
||||
get-caller-file@^2.0.1, get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
@ -7374,11 +7375,6 @@ is-url@^1.2.2:
|
|||
resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
|
||||
integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
|
||||
|
||||
is-utf8@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
|
||||
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
|
||||
|
||||
is-whitespace-character@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7"
|
||||
|
@ -8137,17 +8133,6 @@ listenercount@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937"
|
||||
integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=
|
||||
|
||||
load-json-file@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||
integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
parse-json "^2.2.0"
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
strip-bom "^2.0.0"
|
||||
|
||||
load-json-file@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
|
||||
|
@ -9824,13 +9809,6 @@ parse-entities@^1.0.2, parse-entities@^1.1.0:
|
|||
is-decimal "^1.0.0"
|
||||
is-hexadecimal "^1.0.0"
|
||||
|
||||
parse-json@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
|
||||
integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
|
||||
dependencies:
|
||||
error-ex "^1.2.0"
|
||||
|
||||
parse-json@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
|
||||
|
@ -9910,13 +9888,6 @@ path-dirname@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
|
||||
integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
|
||||
|
||||
path-exists@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
|
||||
integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=
|
||||
dependencies:
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
path-exists@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
|
||||
|
@ -9959,15 +9930,6 @@ path-to-regexp@^1.7.0:
|
|||
dependencies:
|
||||
isarray "0.0.1"
|
||||
|
||||
path-type@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
|
||||
integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
pify "^2.0.0"
|
||||
pinkie-promise "^2.0.0"
|
||||
|
||||
path-type@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
|
||||
|
@ -10872,23 +10834,6 @@ read-package-tree@5.3.1:
|
|||
readdir-scoped-modules "^1.0.0"
|
||||
util-promisify "^2.1.0"
|
||||
|
||||
read-pkg-up@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
||||
integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=
|
||||
dependencies:
|
||||
find-up "^1.0.0"
|
||||
read-pkg "^1.0.0"
|
||||
|
||||
read-pkg@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
|
||||
integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=
|
||||
dependencies:
|
||||
load-json-file "^1.0.0"
|
||||
normalize-package-data "^2.3.2"
|
||||
path-type "^1.0.0"
|
||||
|
||||
read-pkg@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
|
||||
|
@ -12381,7 +12326,7 @@ string-length@^1.0.0:
|
|||
dependencies:
|
||||
strip-ansi "^3.0.0"
|
||||
|
||||
string-width@^1.0.1, string-width@^1.0.2:
|
||||
string-width@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
|
||||
|
@ -12513,13 +12458,6 @@ strip-ansi@^6.0.0:
|
|||
dependencies:
|
||||
ansi-regex "^5.0.0"
|
||||
|
||||
strip-bom@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
|
||||
integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=
|
||||
dependencies:
|
||||
is-utf8 "^0.2.0"
|
||||
|
||||
strip-bom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||
|
@ -14010,11 +13948,6 @@ when@~3.6.x:
|
|||
resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e"
|
||||
integrity sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=
|
||||
|
||||
which-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
|
||||
integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
|
@ -14114,6 +14047,15 @@ wrap-ansi@^6.2.0:
|
|||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
@ -14246,7 +14188,7 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1:
|
|||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
|
||||
y18n@^3.2.0, y18n@^3.2.1:
|
||||
y18n@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
|
||||
integrity sha1-bRX7qITAhnnA136I53WegR4H+kE=
|
||||
|
@ -14256,6 +14198,11 @@ y18n@^3.2.0, y18n@^3.2.1:
|
|||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
|
||||
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
|
||||
|
||||
y18n@^5.0.2:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.3.tgz#978115b82befe2b5c762bf55980b7b01a4a2d5d9"
|
||||
integrity sha512-JeFbcHQ/7hVmMBXW6UB6Tg7apStHd/ztGz1JN78y3pFi/q0Ht1eA6PVkvw56gm7UA8fcJR/ziRlYEDMGoju0yQ==
|
||||
|
||||
yallist@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||
|
@ -14295,12 +14242,10 @@ yargs-parser@^18.1.0, yargs-parser@^18.1.1, yargs-parser@^18.1.3:
|
|||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
|
||||
integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=
|
||||
dependencies:
|
||||
camelcase "^3.0.0"
|
||||
yargs-parser@^20.2.2:
|
||||
version "20.2.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.2.tgz#84562c6b1c41ccec2f13d346c7dd83f8d1a0dc70"
|
||||
integrity sha512-XmrpXaTl6noDsf1dKpBuUNCOHqjs0g3jRMXf/ztRxdOmb+er8kE5z5b55Lz3p5u2T8KJ59ENBnASS8/iapVJ5g==
|
||||
|
||||
yargs@15.3.0:
|
||||
version "15.3.0"
|
||||
|
@ -14383,24 +14328,18 @@ yargs@^15.3.1:
|
|||
y18n "^4.0.0"
|
||||
yargs-parser "^18.1.1"
|
||||
|
||||
yargs@^7.0.2:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
|
||||
integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=
|
||||
yargs@^16.1.0:
|
||||
version "16.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.1.0.tgz#fc333fe4791660eace5a894b39d42f851cd48f2a"
|
||||
integrity sha512-upWFJOmDdHN0syLuESuvXDmrRcWd1QafJolHskzaw79uZa7/x53gxQKiR07W59GWY1tFhhU/Th9DrtSfpS782g==
|
||||
dependencies:
|
||||
camelcase "^3.0.0"
|
||||
cliui "^3.2.0"
|
||||
decamelize "^1.1.1"
|
||||
get-caller-file "^1.0.1"
|
||||
os-locale "^1.4.0"
|
||||
read-pkg-up "^1.0.1"
|
||||
cliui "^7.0.2"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^1.0.1"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^1.0.2"
|
||||
which-module "^1.0.0"
|
||||
y18n "^3.2.1"
|
||||
yargs-parser "^5.0.0"
|
||||
string-width "^4.2.0"
|
||||
y18n "^5.0.2"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yauzl@^2.10.0:
|
||||
version "2.10.0"
|
||||
|
|
Loading…
Reference in New Issue