semmet-angular 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -19,6 +19,8 @@ Brazilian Portuguese documentation is available in [README.md](./README.md).
19
19
  ```
20
20
  ng add semmet-angular
21
21
  ng generate semmet-angular:accordion my-faq
22
+ ng generate semmet-angular:ci
23
+ ng generate semmet-angular:docker
22
24
  ```
23
25
 
24
26
  `ng add semmet-angular` installs the package and configures the delivery workflow once for the target application, including Playwright e2e support and npm scripts. Each generated component is dropped into your project exactly like `ng generate component` would (respecting your `angular.json` project, source root, and selector prefix), and has **zero runtime dependency** on this package — `semmet-angular` is only needed at generation time.
@@ -47,6 +49,26 @@ npm run e2e:ui
47
49
 
48
50
  `npm run e2e` runs Playwright against the Angular dev server configured by `playwright.config.ts`; `npm run e2e:ui` opens Playwright's interactive UI.
49
51
 
52
+ ## GitHub Actions CI
53
+
54
+ ```bash
55
+ ng generate semmet-angular:ci
56
+ ```
57
+
58
+ This command generates `.github/workflows/ci.yml` using the package scripts that already exist in `package.json`. The workflow detects the package manager by lockfile (`package-lock.json`, `pnpm-lock.yaml`, or `yarn.lock`), installs dependencies, and runs `verify` when available. If `verify` does not exist yet, it runs `build`, `test`, and `e2e` when those scripts exist.
59
+
60
+ By default, the schematic does not overwrite an existing workflow. Use `--force` when you want to replace `.github/workflows/ci.yml`.
61
+
62
+ ## Docker Packaging
63
+
64
+ ```bash
65
+ ng generate semmet-angular:docker
66
+ ```
67
+
68
+ This command generates `Dockerfile`, `.dockerignore`, and `nginx.conf` to package the Angular application as an image served by Nginx. The Dockerfile detects the package manager by lockfile, runs the build in the Node stage, and copies the output configured in `angular.json` to `/usr/share/nginx/html`.
69
+
70
+ The `nginx.conf` includes SPA fallback with `try_files ... /index.html`. By default, the schematic does not overwrite existing files; use `--force` to replace the current Docker configuration.
71
+
50
72
  ## Projected content containers
51
73
 
52
74
  Some generated components are containers: they own the ARIA wiring, keyboard behavior, focus management, and visual shell, while your app owns the content rendered inside them.
package/README.md CHANGED
@@ -19,6 +19,8 @@ English documentation is available in [README.en.md](./README.en.md).
19
19
  ```bash
20
20
  ng add semmet-angular
21
21
  ng generate semmet-angular:accordion minha-faq
22
+ ng generate semmet-angular:ci
23
+ ng generate semmet-angular:docker
22
24
  ```
23
25
 
24
26
  `ng add semmet-angular` instala o pacote e configura uma vez o fluxo de entrega da aplicação alvo, incluindo suporte a e2e com Playwright e scripts npm. Cada componente gerado entra no seu projeto do mesmo jeito que entraria com `ng generate component` (respeitando `angular.json`, `sourceRoot` e prefixo de seletor), e tem **zero dependência em tempo de execução** deste pacote — `semmet-angular` só é necessário na geração.
@@ -47,6 +49,26 @@ npm run lint
47
49
 
48
50
  `lint` só entra quando a aplicação já tem um script `lint`; `build` e `test` seguem a mesma lógica. O `e2e` entra depois da configuração do Playwright feita pelo `ng add`.
49
51
 
52
+ ## CI com GitHub Actions
53
+
54
+ ```bash
55
+ ng generate semmet-angular:ci
56
+ ```
57
+
58
+ Esse comando gera `.github/workflows/ci.yml` usando os scripts já existentes no `package.json`. O workflow detecta o gerenciador de pacotes por lockfile (`package-lock.json`, `pnpm-lock.yaml` ou `yarn.lock`), instala dependências e executa `verify` quando disponível. Se `verify` ainda não existir, ele roda `build`, `test` e `e2e` conforme esses scripts existirem.
59
+
60
+ Por padrão, o schematic não sobrescreve um workflow existente. Use `--force` quando quiser substituir `.github/workflows/ci.yml`.
61
+
62
+ ## Empacotamento com Docker
63
+
64
+ ```bash
65
+ ng generate semmet-angular:docker
66
+ ```
67
+
68
+ Esse comando gera `Dockerfile`, `.dockerignore` e `nginx.conf` para empacotar a aplicação Angular como uma imagem servida por Nginx. O Dockerfile detecta o gerenciador de pacotes por lockfile, executa o build no estágio Node e copia o output configurado no `angular.json` para `/usr/share/nginx/html`.
69
+
70
+ O `nginx.conf` inclui fallback de SPA com `try_files ... /index.html`. Por padrão, o schematic não sobrescreve arquivos existentes; use `--force` para substituir a configuração Docker atual.
71
+
50
72
  ## Testes Gerados
51
73
 
52
74
  Todo schematic de componente gera arquivos de teste ao lado do componente:
package/README.pt-BR.md CHANGED
@@ -17,6 +17,8 @@ Leia a direção geral do produto em [PRODUCT_VISION.md](./PRODUCT_VISION.md).
17
17
  ```bash
18
18
  ng add semmet-angular
19
19
  ng generate semmet-angular:accordion minha-faq
20
+ ng generate semmet-angular:ci
21
+ ng generate semmet-angular:docker
20
22
  ```
21
23
 
22
24
  `ng add semmet-angular` instala o pacote e configura uma vez o fluxo de entrega da aplicação alvo, incluindo suporte a e2e com Playwright e scripts npm. Cada componente gerado entra no seu projeto do mesmo jeito que entraria com `ng generate component` (respeitando `angular.json`, `sourceRoot` e prefixo de seletor), e tem **zero dependência em tempo de execução** deste pacote — `semmet-angular` só é necessário na geração.
@@ -45,6 +47,26 @@ npm run lint
45
47
 
46
48
  `lint` só entra quando a aplicação já tem um script `lint`; `build` e `test` seguem a mesma lógica. O `e2e` entra depois da configuração do Playwright feita pelo `ng add`.
47
49
 
50
+ ## CI com GitHub Actions
51
+
52
+ ```bash
53
+ ng generate semmet-angular:ci
54
+ ```
55
+
56
+ Esse comando gera `.github/workflows/ci.yml` usando os scripts já existentes no `package.json`. O workflow detecta o gerenciador de pacotes por lockfile (`package-lock.json`, `pnpm-lock.yaml` ou `yarn.lock`), instala dependências e executa `verify` quando disponível. Se `verify` ainda não existir, ele roda `build`, `test` e `e2e` conforme esses scripts existirem.
57
+
58
+ Por padrão, o schematic não sobrescreve um workflow existente. Use `--force` quando quiser substituir `.github/workflows/ci.yml`.
59
+
60
+ ## Empacotamento com Docker
61
+
62
+ ```bash
63
+ ng generate semmet-angular:docker
64
+ ```
65
+
66
+ Esse comando gera `Dockerfile`, `.dockerignore` e `nginx.conf` para empacotar a aplicação Angular como uma imagem servida por Nginx. O Dockerfile detecta o gerenciador de pacotes por lockfile, executa o build no estágio Node e copia o output configurado no `angular.json` para `/usr/share/nginx/html`.
67
+
68
+ O `nginx.conf` inclui fallback de SPA com `try_files ... /index.html`. Por padrão, o schematic não sobrescreve arquivos existentes; use `--force` para substituir a configuração Docker atual.
69
+
48
70
  ## Testes Gerados
49
71
 
50
72
  Todo schematic de componente gera arquivos de teste ao lado do componente:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semmet-angular",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
4
4
  "description": "Accessible UI Delivery Kit for Angular: schematics that generate ARIA-conformant standalone components with styling, keyboard behavior, unit tests, and Playwright e2e smoke tests.",
5
5
  "publisher": "danilodevsilva",
6
6
  "license": "MIT",
@@ -0,0 +1,3 @@
1
+ import { Rule } from '@angular-devkit/schematics';
2
+ import { Schema } from './schema';
3
+ export declare function ci(options: Schema): Rule;
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ci = ci;
4
+ const schematics_1 = require("@angular-devkit/schematics");
5
+ function ci(options) {
6
+ return (tree) => {
7
+ const workflowPath = buildWorkflowPath(options.name);
8
+ if (tree.exists(workflowPath) && !options.force) {
9
+ throw new schematics_1.SchematicsException(`${workflowPath} already exists. Re-run with --force to overwrite it.`);
10
+ }
11
+ const packageJson = readJson(tree, '/package.json');
12
+ const scripts = packageJson.scripts ?? {};
13
+ const packageManager = detectPackageManager(tree);
14
+ const workflow = buildWorkflow(packageManager, Object.keys(scripts), tree.exists('/package-lock.json'));
15
+ if (tree.exists(workflowPath)) {
16
+ tree.overwrite(workflowPath, workflow);
17
+ }
18
+ else {
19
+ tree.create(workflowPath, workflow);
20
+ }
21
+ };
22
+ }
23
+ function buildWorkflowPath(name = 'ci') {
24
+ const normalizedName = name.trim().replace(/\.ya?ml$/, '');
25
+ if (!normalizedName || normalizedName.includes('/') || normalizedName.includes('\\')) {
26
+ throw new schematics_1.SchematicsException('CI workflow name must be a file name without path separators.');
27
+ }
28
+ return `/.github/workflows/${normalizedName}.yml`;
29
+ }
30
+ function detectPackageManager(tree) {
31
+ if (tree.exists('/pnpm-lock.yaml')) {
32
+ return 'pnpm';
33
+ }
34
+ if (tree.exists('/yarn.lock')) {
35
+ return 'yarn';
36
+ }
37
+ return 'npm';
38
+ }
39
+ function buildWorkflow(packageManager, scriptNames, hasNpmLockfile) {
40
+ const commands = getPackageManagerCommands(packageManager, hasNpmLockfile);
41
+ const runSteps = buildRunSteps(commands, scriptNames);
42
+ const setupPackageManagerStep = commands.setupStep ? `${commands.setupStep}\n` : '';
43
+ const cacheConfig = commands.cache ? ` cache: ${commands.cache}\n` : '';
44
+ return `name: CI
45
+
46
+ on:
47
+ push:
48
+ branches: [main]
49
+ pull_request:
50
+
51
+ jobs:
52
+ verify:
53
+ runs-on: ubuntu-latest
54
+
55
+ steps:
56
+ - name: Checkout
57
+ uses: actions/checkout@v4
58
+
59
+ ${setupPackageManagerStep} - name: Setup Node
60
+ uses: actions/setup-node@v4
61
+ with:
62
+ node-version: 20
63
+ ${cacheConfig}
64
+
65
+ - name: Install dependencies
66
+ run: ${commands.install}
67
+ ${runSteps}
68
+ `;
69
+ }
70
+ function getPackageManagerCommands(packageManager, hasNpmLockfile) {
71
+ if (packageManager === 'pnpm') {
72
+ return {
73
+ install: 'pnpm install --frozen-lockfile',
74
+ run: (script) => `pnpm run ${script}`,
75
+ setupStep: ` - name: Setup pnpm
76
+ uses: pnpm/action-setup@v4
77
+ with:
78
+ version: 9`,
79
+ cache: 'pnpm',
80
+ };
81
+ }
82
+ if (packageManager === 'yarn') {
83
+ return {
84
+ install: 'yarn install --frozen-lockfile',
85
+ run: (script) => `yarn ${script}`,
86
+ cache: 'yarn',
87
+ };
88
+ }
89
+ return {
90
+ install: hasNpmLockfile ? 'npm ci' : 'npm install',
91
+ run: (script) => `npm run ${script}`,
92
+ cache: hasNpmLockfile ? 'npm' : undefined,
93
+ };
94
+ }
95
+ function buildRunSteps(commands, scriptNames) {
96
+ const scripts = new Set(scriptNames);
97
+ const verificationScripts = scripts.has('verify')
98
+ ? ['verify']
99
+ : ['build', 'test', 'e2e'].filter((script) => scripts.has(script));
100
+ if (verificationScripts.length === 0) {
101
+ return `
102
+ - name: Check package scripts
103
+ run: echo "No verify, build, test, or e2e script found in package.json."
104
+ `;
105
+ }
106
+ return verificationScripts
107
+ .map((script) => `
108
+ - name: Run ${script}
109
+ run: ${commands.run(script)}
110
+ `)
111
+ .join('');
112
+ }
113
+ function readJson(tree, path) {
114
+ const content = tree.readText(path);
115
+ return JSON.parse(content);
116
+ }
117
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
2
+ import { Schema } from './schema';
3
+
4
+ type PackageManager = 'npm' | 'pnpm' | 'yarn';
5
+
6
+ interface PackageManagerCommands {
7
+ install: string;
8
+ run: (script: string) => string;
9
+ setupStep?: string;
10
+ cache?: string;
11
+ }
12
+
13
+ export function ci(options: Schema): Rule {
14
+ return (tree: Tree) => {
15
+ const workflowPath = buildWorkflowPath(options.name);
16
+
17
+ if (tree.exists(workflowPath) && !options.force) {
18
+ throw new SchematicsException(`${workflowPath} already exists. Re-run with --force to overwrite it.`);
19
+ }
20
+
21
+ const packageJson = readJson(tree, '/package.json');
22
+ const scripts = packageJson.scripts ?? {};
23
+ const packageManager = detectPackageManager(tree);
24
+ const workflow = buildWorkflow(packageManager, Object.keys(scripts), tree.exists('/package-lock.json'));
25
+
26
+ if (tree.exists(workflowPath)) {
27
+ tree.overwrite(workflowPath, workflow);
28
+ } else {
29
+ tree.create(workflowPath, workflow);
30
+ }
31
+ };
32
+ }
33
+
34
+ function buildWorkflowPath(name = 'ci'): string {
35
+ const normalizedName = name.trim().replace(/\.ya?ml$/, '');
36
+
37
+ if (!normalizedName || normalizedName.includes('/') || normalizedName.includes('\\')) {
38
+ throw new SchematicsException('CI workflow name must be a file name without path separators.');
39
+ }
40
+
41
+ return `/.github/workflows/${normalizedName}.yml`;
42
+ }
43
+
44
+ function detectPackageManager(tree: Tree): PackageManager {
45
+ if (tree.exists('/pnpm-lock.yaml')) {
46
+ return 'pnpm';
47
+ }
48
+
49
+ if (tree.exists('/yarn.lock')) {
50
+ return 'yarn';
51
+ }
52
+
53
+ return 'npm';
54
+ }
55
+
56
+ function buildWorkflow(packageManager: PackageManager, scriptNames: string[], hasNpmLockfile: boolean): string {
57
+ const commands = getPackageManagerCommands(packageManager, hasNpmLockfile);
58
+ const runSteps = buildRunSteps(commands, scriptNames);
59
+ const setupPackageManagerStep = commands.setupStep ? `${commands.setupStep}\n` : '';
60
+ const cacheConfig = commands.cache ? ` cache: ${commands.cache}\n` : '';
61
+
62
+ return `name: CI
63
+
64
+ on:
65
+ push:
66
+ branches: [main]
67
+ pull_request:
68
+
69
+ jobs:
70
+ verify:
71
+ runs-on: ubuntu-latest
72
+
73
+ steps:
74
+ - name: Checkout
75
+ uses: actions/checkout@v4
76
+
77
+ ${setupPackageManagerStep} - name: Setup Node
78
+ uses: actions/setup-node@v4
79
+ with:
80
+ node-version: 20
81
+ ${cacheConfig}
82
+
83
+ - name: Install dependencies
84
+ run: ${commands.install}
85
+ ${runSteps}
86
+ `;
87
+ }
88
+
89
+ function getPackageManagerCommands(packageManager: PackageManager, hasNpmLockfile: boolean): PackageManagerCommands {
90
+ if (packageManager === 'pnpm') {
91
+ return {
92
+ install: 'pnpm install --frozen-lockfile',
93
+ run: (script) => `pnpm run ${script}`,
94
+ setupStep: ` - name: Setup pnpm
95
+ uses: pnpm/action-setup@v4
96
+ with:
97
+ version: 9`,
98
+ cache: 'pnpm',
99
+ };
100
+ }
101
+
102
+ if (packageManager === 'yarn') {
103
+ return {
104
+ install: 'yarn install --frozen-lockfile',
105
+ run: (script) => `yarn ${script}`,
106
+ cache: 'yarn',
107
+ };
108
+ }
109
+
110
+ return {
111
+ install: hasNpmLockfile ? 'npm ci' : 'npm install',
112
+ run: (script) => `npm run ${script}`,
113
+ cache: hasNpmLockfile ? 'npm' : undefined,
114
+ };
115
+ }
116
+
117
+ function buildRunSteps(commands: PackageManagerCommands, scriptNames: string[]): string {
118
+ const scripts = new Set(scriptNames);
119
+ const verificationScripts = scripts.has('verify')
120
+ ? ['verify']
121
+ : ['build', 'test', 'e2e'].filter((script) => scripts.has(script));
122
+
123
+ if (verificationScripts.length === 0) {
124
+ return `
125
+ - name: Check package scripts
126
+ run: echo "No verify, build, test, or e2e script found in package.json."
127
+ `;
128
+ }
129
+
130
+ return verificationScripts
131
+ .map(
132
+ (script) => `
133
+ - name: Run ${script}
134
+ run: ${commands.run(script)}
135
+ `
136
+ )
137
+ .join('');
138
+ }
139
+
140
+ function readJson(tree: Tree, path: string): Record<string, any> {
141
+ const content = tree.readText(path);
142
+ return JSON.parse(content) as Record<string, any>;
143
+ }
@@ -0,0 +1,4 @@
1
+ export interface Schema {
2
+ force?: boolean;
3
+ name?: string;
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "SemmetAngularCi",
4
+ "title": "Semmet Angular CI Options Schema",
5
+ "type": "object",
6
+ "description": "Generates a GitHub Actions CI workflow using the target workspace package scripts.",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "force": {
10
+ "type": "boolean",
11
+ "default": false,
12
+ "description": "Overwrite an existing CI workflow."
13
+ },
14
+ "name": {
15
+ "type": "string",
16
+ "default": "ci",
17
+ "description": "The workflow file name to create under .github/workflows."
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,4 @@
1
+ export interface Schema {
2
+ force?: boolean;
3
+ name?: string;
4
+ }
@@ -6,6 +6,16 @@
6
6
  "factory": "./ng-add/index#ngAdd",
7
7
  "schema": "./ng-add/schema.json"
8
8
  },
9
+ "ci": {
10
+ "description": "Generates a GitHub Actions CI workflow using existing package scripts.",
11
+ "factory": "./ci/index#ci",
12
+ "schema": "./ci/schema.json"
13
+ },
14
+ "docker": {
15
+ "description": "Generates Docker packaging files for an Angular application served by Nginx.",
16
+ "factory": "./docker/index#docker",
17
+ "schema": "./docker/schema.json"
18
+ },
9
19
  "accordion": {
10
20
  "description": "Generates a standalone Angular Accordion component following the W3C ARIA Authoring Practices Guide.",
11
21
  "factory": "./accordion/index#accordion",
@@ -0,0 +1,3 @@
1
+ import { Rule } from '@angular-devkit/schematics';
2
+ import { Schema } from './schema';
3
+ export declare function docker(options: Schema): Rule;
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.docker = docker;
4
+ const schematics_1 = require("@angular-devkit/schematics");
5
+ const DOCKERFILE_PATH = '/Dockerfile';
6
+ const DOCKERIGNORE_PATH = '/.dockerignore';
7
+ const NGINX_CONFIG_PATH = '/nginx.conf';
8
+ function docker(options) {
9
+ return (tree) => {
10
+ const files = [DOCKERFILE_PATH, DOCKERIGNORE_PATH, NGINX_CONFIG_PATH];
11
+ const existingFiles = files.filter((file) => tree.exists(file));
12
+ if (existingFiles.length > 0 && !options.force) {
13
+ throw new schematics_1.SchematicsException(`${existingFiles.join(', ')} already exist. Re-run with --force to overwrite them.`);
14
+ }
15
+ const workspace = readJson(tree, '/angular.json');
16
+ const projectName = resolveProjectName(workspace, options.project);
17
+ const outputPath = resolveBuildOutputPath(workspace, projectName);
18
+ const packageManager = detectPackageManager(tree);
19
+ const commands = getPackageManagerCommands(packageManager, tree.exists('/package-lock.json'));
20
+ writeFile(tree, DOCKERFILE_PATH, buildDockerfile(outputPath, commands));
21
+ writeFile(tree, DOCKERIGNORE_PATH, buildDockerignore());
22
+ writeFile(tree, NGINX_CONFIG_PATH, buildNginxConfig());
23
+ };
24
+ }
25
+ function resolveProjectName(workspace, projectName) {
26
+ if (projectName) {
27
+ if (!workspace.projects?.[projectName]) {
28
+ throw new schematics_1.SchematicsException(`Project "${projectName}" was not found in angular.json.`);
29
+ }
30
+ return projectName;
31
+ }
32
+ if (workspace.defaultProject && workspace.projects?.[workspace.defaultProject]) {
33
+ return workspace.defaultProject;
34
+ }
35
+ const projects = Object.keys(workspace.projects ?? {});
36
+ if (projects.length === 0) {
37
+ throw new schematics_1.SchematicsException('No Angular project was found in angular.json.');
38
+ }
39
+ return projects[0];
40
+ }
41
+ function resolveBuildOutputPath(workspace, projectName) {
42
+ const project = workspace.projects?.[projectName] ?? {};
43
+ const buildTarget = project.architect?.build ?? project.targets?.build;
44
+ const outputPath = buildTarget?.options?.outputPath;
45
+ if (typeof outputPath === 'string') {
46
+ return trimSlashes(outputPath);
47
+ }
48
+ if (outputPath?.base) {
49
+ return trimSlashes([outputPath.base, outputPath.browser].filter(Boolean).join('/'));
50
+ }
51
+ return `dist/${projectName}/browser`;
52
+ }
53
+ function detectPackageManager(tree) {
54
+ if (tree.exists('/pnpm-lock.yaml')) {
55
+ return 'pnpm';
56
+ }
57
+ if (tree.exists('/yarn.lock')) {
58
+ return 'yarn';
59
+ }
60
+ return 'npm';
61
+ }
62
+ function getPackageManagerCommands(packageManager, hasNpmLockfile) {
63
+ if (packageManager === 'pnpm') {
64
+ return {
65
+ prepare: 'RUN corepack enable',
66
+ install: 'pnpm install --frozen-lockfile',
67
+ build: 'pnpm run build',
68
+ };
69
+ }
70
+ if (packageManager === 'yarn') {
71
+ return {
72
+ prepare: 'RUN corepack enable',
73
+ install: 'yarn install --frozen-lockfile',
74
+ build: 'yarn build',
75
+ };
76
+ }
77
+ return {
78
+ install: hasNpmLockfile ? 'npm ci' : 'npm install',
79
+ build: 'npm run build',
80
+ };
81
+ }
82
+ function buildDockerfile(outputPath, commands) {
83
+ const prepareStep = commands.prepare ? `${commands.prepare}\n` : '';
84
+ return `FROM node:20-alpine AS build
85
+ WORKDIR /app
86
+
87
+ COPY package*.json ./
88
+ COPY pnpm-lock.yaml* yarn.lock* ./
89
+ ${prepareStep}RUN ${commands.install}
90
+
91
+ COPY . .
92
+ RUN ${commands.build}
93
+
94
+ FROM nginx:1.27-alpine
95
+ COPY nginx.conf /etc/nginx/conf.d/default.conf
96
+ COPY --from=build /app/${outputPath} /usr/share/nginx/html
97
+
98
+ EXPOSE 80
99
+
100
+ CMD ["nginx", "-g", "daemon off;"]
101
+ `;
102
+ }
103
+ function buildDockerignore() {
104
+ return `node_modules
105
+ dist
106
+ .angular
107
+ .git
108
+ .github
109
+ coverage
110
+ playwright-report
111
+ test-results
112
+ .DS_Store
113
+ npm-debug.log*
114
+ yarn-debug.log*
115
+ yarn-error.log*
116
+ `;
117
+ }
118
+ function buildNginxConfig() {
119
+ return `server {
120
+ listen 80;
121
+ server_name _;
122
+
123
+ root /usr/share/nginx/html;
124
+ index index.html;
125
+
126
+ location / {
127
+ try_files $uri $uri/ /index.html;
128
+ }
129
+
130
+ location ~* \\.(?:css|js|mjs|map|ico|png|jpg|jpeg|gif|svg|webp|woff2?)$ {
131
+ try_files $uri =404;
132
+ access_log off;
133
+ expires 1y;
134
+ add_header Cache-Control "public, immutable";
135
+ }
136
+ }
137
+ `;
138
+ }
139
+ function writeFile(tree, path, content) {
140
+ if (tree.exists(path)) {
141
+ tree.overwrite(path, content);
142
+ }
143
+ else {
144
+ tree.create(path, content);
145
+ }
146
+ }
147
+ function trimSlashes(value) {
148
+ return value.replace(/^\/+|\/+$/g, '');
149
+ }
150
+ function readJson(tree, path) {
151
+ const content = tree.readText(path);
152
+ return JSON.parse(content);
153
+ }
154
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,186 @@
1
+ import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
2
+ import { Schema } from './schema';
3
+
4
+ type PackageManager = 'npm' | 'pnpm' | 'yarn';
5
+
6
+ interface PackageManagerCommands {
7
+ install: string;
8
+ build: string;
9
+ prepare?: string;
10
+ }
11
+
12
+ const DOCKERFILE_PATH = '/Dockerfile';
13
+ const DOCKERIGNORE_PATH = '/.dockerignore';
14
+ const NGINX_CONFIG_PATH = '/nginx.conf';
15
+
16
+ export function docker(options: Schema): Rule {
17
+ return (tree: Tree) => {
18
+ const files = [DOCKERFILE_PATH, DOCKERIGNORE_PATH, NGINX_CONFIG_PATH];
19
+ const existingFiles = files.filter((file) => tree.exists(file));
20
+
21
+ if (existingFiles.length > 0 && !options.force) {
22
+ throw new SchematicsException(`${existingFiles.join(', ')} already exist. Re-run with --force to overwrite them.`);
23
+ }
24
+
25
+ const workspace = readJson(tree, '/angular.json');
26
+ const projectName = resolveProjectName(workspace, options.project);
27
+ const outputPath = resolveBuildOutputPath(workspace, projectName);
28
+ const packageManager = detectPackageManager(tree);
29
+ const commands = getPackageManagerCommands(packageManager, tree.exists('/package-lock.json'));
30
+
31
+ writeFile(tree, DOCKERFILE_PATH, buildDockerfile(outputPath, commands));
32
+ writeFile(tree, DOCKERIGNORE_PATH, buildDockerignore());
33
+ writeFile(tree, NGINX_CONFIG_PATH, buildNginxConfig());
34
+ };
35
+ }
36
+
37
+ function resolveProjectName(workspace: Record<string, any>, projectName?: string): string {
38
+ if (projectName) {
39
+ if (!workspace.projects?.[projectName]) {
40
+ throw new SchematicsException(`Project "${projectName}" was not found in angular.json.`);
41
+ }
42
+
43
+ return projectName;
44
+ }
45
+
46
+ if (workspace.defaultProject && workspace.projects?.[workspace.defaultProject]) {
47
+ return workspace.defaultProject as string;
48
+ }
49
+
50
+ const projects = Object.keys(workspace.projects ?? {});
51
+
52
+ if (projects.length === 0) {
53
+ throw new SchematicsException('No Angular project was found in angular.json.');
54
+ }
55
+
56
+ return projects[0];
57
+ }
58
+
59
+ function resolveBuildOutputPath(workspace: Record<string, any>, projectName: string): string {
60
+ const project = workspace.projects?.[projectName] ?? {};
61
+ const buildTarget = project.architect?.build ?? project.targets?.build;
62
+ const outputPath = buildTarget?.options?.outputPath;
63
+
64
+ if (typeof outputPath === 'string') {
65
+ return trimSlashes(outputPath);
66
+ }
67
+
68
+ if (outputPath?.base) {
69
+ return trimSlashes([outputPath.base, outputPath.browser].filter(Boolean).join('/'));
70
+ }
71
+
72
+ return `dist/${projectName}/browser`;
73
+ }
74
+
75
+ function detectPackageManager(tree: Tree): PackageManager {
76
+ if (tree.exists('/pnpm-lock.yaml')) {
77
+ return 'pnpm';
78
+ }
79
+
80
+ if (tree.exists('/yarn.lock')) {
81
+ return 'yarn';
82
+ }
83
+
84
+ return 'npm';
85
+ }
86
+
87
+ function getPackageManagerCommands(packageManager: PackageManager, hasNpmLockfile: boolean): PackageManagerCommands {
88
+ if (packageManager === 'pnpm') {
89
+ return {
90
+ prepare: 'RUN corepack enable',
91
+ install: 'pnpm install --frozen-lockfile',
92
+ build: 'pnpm run build',
93
+ };
94
+ }
95
+
96
+ if (packageManager === 'yarn') {
97
+ return {
98
+ prepare: 'RUN corepack enable',
99
+ install: 'yarn install --frozen-lockfile',
100
+ build: 'yarn build',
101
+ };
102
+ }
103
+
104
+ return {
105
+ install: hasNpmLockfile ? 'npm ci' : 'npm install',
106
+ build: 'npm run build',
107
+ };
108
+ }
109
+
110
+ function buildDockerfile(outputPath: string, commands: PackageManagerCommands): string {
111
+ const prepareStep = commands.prepare ? `${commands.prepare}\n` : '';
112
+
113
+ return `FROM node:20-alpine AS build
114
+ WORKDIR /app
115
+
116
+ COPY package*.json ./
117
+ COPY pnpm-lock.yaml* yarn.lock* ./
118
+ ${prepareStep}RUN ${commands.install}
119
+
120
+ COPY . .
121
+ RUN ${commands.build}
122
+
123
+ FROM nginx:1.27-alpine
124
+ COPY nginx.conf /etc/nginx/conf.d/default.conf
125
+ COPY --from=build /app/${outputPath} /usr/share/nginx/html
126
+
127
+ EXPOSE 80
128
+
129
+ CMD ["nginx", "-g", "daemon off;"]
130
+ `;
131
+ }
132
+
133
+ function buildDockerignore(): string {
134
+ return `node_modules
135
+ dist
136
+ .angular
137
+ .git
138
+ .github
139
+ coverage
140
+ playwright-report
141
+ test-results
142
+ .DS_Store
143
+ npm-debug.log*
144
+ yarn-debug.log*
145
+ yarn-error.log*
146
+ `;
147
+ }
148
+
149
+ function buildNginxConfig(): string {
150
+ return `server {
151
+ listen 80;
152
+ server_name _;
153
+
154
+ root /usr/share/nginx/html;
155
+ index index.html;
156
+
157
+ location / {
158
+ try_files $uri $uri/ /index.html;
159
+ }
160
+
161
+ location ~* \\.(?:css|js|mjs|map|ico|png|jpg|jpeg|gif|svg|webp|woff2?)$ {
162
+ try_files $uri =404;
163
+ access_log off;
164
+ expires 1y;
165
+ add_header Cache-Control "public, immutable";
166
+ }
167
+ }
168
+ `;
169
+ }
170
+
171
+ function writeFile(tree: Tree, path: string, content: string): void {
172
+ if (tree.exists(path)) {
173
+ tree.overwrite(path, content);
174
+ } else {
175
+ tree.create(path, content);
176
+ }
177
+ }
178
+
179
+ function trimSlashes(value: string): string {
180
+ return value.replace(/^\/+|\/+$/g, '');
181
+ }
182
+
183
+ function readJson(tree: Tree, path: string): Record<string, any> {
184
+ const content = tree.readText(path);
185
+ return JSON.parse(content) as Record<string, any>;
186
+ }
@@ -0,0 +1,4 @@
1
+ export interface Schema {
2
+ force?: boolean;
3
+ project?: string;
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["schema.ts"],"names":[],"mappings":""}
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$id": "SemmetAngularDocker",
4
+ "title": "Semmet Angular Docker Options Schema",
5
+ "type": "object",
6
+ "description": "Generates Docker packaging files for an Angular application served by Nginx.",
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "force": {
10
+ "type": "boolean",
11
+ "default": false,
12
+ "description": "Overwrite existing Docker packaging files."
13
+ },
14
+ "project": {
15
+ "type": "string",
16
+ "description": "The Angular project to package.",
17
+ "$default": { "$source": "projectName" }
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,4 @@
1
+ export interface Schema {
2
+ force?: boolean;
3
+ project?: string;
4
+ }