semmet-angular 0.30.0 → 0.32.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 +20 -1
- package/README.md +20 -1
- package/README.pt-BR.md +20 -1
- package/package.json +1 -1
- package/src/collection.json +5 -0
- package/src/deploy/index.d.ts +3 -0
- package/src/deploy/index.js +245 -0
- package/src/deploy/index.js.map +1 -0
- package/src/deploy/index.ts +296 -0
- package/src/deploy/schema.d.ts +6 -0
- package/src/deploy/schema.js +3 -0
- package/src/deploy/schema.js.map +1 -0
- package/src/deploy/schema.json +37 -0
- package/src/deploy/schema.ts +7 -0
- package/src/docker/index.js +1 -1
- package/src/docker/index.ts +1 -1
package/README.en.md
CHANGED
|
@@ -21,6 +21,7 @@ ng add semmet-angular
|
|
|
21
21
|
ng generate semmet-angular:accordion my-faq
|
|
22
22
|
ng generate semmet-angular:ci
|
|
23
23
|
ng generate semmet-angular:docker
|
|
24
|
+
ng generate semmet-angular:deploy
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
`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.
|
|
@@ -65,10 +66,28 @@ By default, the schematic does not overwrite an existing workflow. Use `--force`
|
|
|
65
66
|
ng generate semmet-angular:docker
|
|
66
67
|
```
|
|
67
68
|
|
|
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
|
|
69
|
+
This command generates `Dockerfile`, `.dockerignore`, and `nginx.conf` to package the Angular application as an image served by Nginx. The Dockerfile uses Node 24 in the build stage, detects the package manager by lockfile, runs the build, and copies the output configured in `angular.json` to `/usr/share/nginx/html`.
|
|
69
70
|
|
|
70
71
|
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
72
|
|
|
73
|
+
## Guided Deploy
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
ng generate semmet-angular:deploy
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The command asks which platform you want to target and generates the minimal configuration for that destination. You can also pass the platform directly:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
ng generate semmet-angular:deploy --platform=vercel
|
|
83
|
+
ng generate semmet-angular:deploy --platform=netlify
|
|
84
|
+
ng generate semmet-angular:deploy --platform=firebase
|
|
85
|
+
ng generate semmet-angular:deploy --platform=github-pages
|
|
86
|
+
ng generate semmet-angular:deploy --platform=docker
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Supported targets: Vercel (`vercel.json`), Netlify (`netlify.toml`), Firebase Hosting (`firebase.json` and `.firebaserc`), GitHub Pages (`.github/workflows/deploy.yml`), and Docker-based deployment (`compose.yml` and `deploy/docker.README.md`). The schematic detects the build output from `angular.json` when possible and does not overwrite existing files without `--force`.
|
|
90
|
+
|
|
72
91
|
## Projected content containers
|
|
73
92
|
|
|
74
93
|
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
|
@@ -21,6 +21,7 @@ ng add semmet-angular
|
|
|
21
21
|
ng generate semmet-angular:accordion minha-faq
|
|
22
22
|
ng generate semmet-angular:ci
|
|
23
23
|
ng generate semmet-angular:docker
|
|
24
|
+
ng generate semmet-angular:deploy
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
`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.
|
|
@@ -65,10 +66,28 @@ Por padrão, o schematic não sobrescreve um workflow existente. Use `--force` q
|
|
|
65
66
|
ng generate semmet-angular:docker
|
|
66
67
|
```
|
|
67
68
|
|
|
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
|
|
69
|
+
Esse comando gera `Dockerfile`, `.dockerignore` e `nginx.conf` para empacotar a aplicação Angular como uma imagem servida por Nginx. O Dockerfile usa Node 24 no estágio de build, detecta o gerenciador de pacotes por lockfile, executa o build e copia o output configurado no `angular.json` para `/usr/share/nginx/html`.
|
|
69
70
|
|
|
70
71
|
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
72
|
|
|
73
|
+
## Deploy Guiado
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
ng generate semmet-angular:deploy
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
O comando pergunta qual plataforma você quer usar e gera a configuração mínima para o destino escolhido. Também dá para passar a plataforma direto:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
ng generate semmet-angular:deploy --platform=vercel
|
|
83
|
+
ng generate semmet-angular:deploy --platform=netlify
|
|
84
|
+
ng generate semmet-angular:deploy --platform=firebase
|
|
85
|
+
ng generate semmet-angular:deploy --platform=github-pages
|
|
86
|
+
ng generate semmet-angular:deploy --platform=docker
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Destinos suportados: Vercel (`vercel.json`), Netlify (`netlify.toml`), Firebase Hosting (`firebase.json` e `.firebaserc`), GitHub Pages (`.github/workflows/deploy.yml`) e deploy baseado em Docker (`compose.yml` e `deploy/docker.README.md`). O schematic detecta o output do build no `angular.json` quando possível e não sobrescreve arquivos existentes sem `--force`.
|
|
90
|
+
|
|
72
91
|
## Testes Gerados
|
|
73
92
|
|
|
74
93
|
Todo schematic de componente gera arquivos de teste ao lado do componente:
|
package/README.pt-BR.md
CHANGED
|
@@ -19,6 +19,7 @@ ng add semmet-angular
|
|
|
19
19
|
ng generate semmet-angular:accordion minha-faq
|
|
20
20
|
ng generate semmet-angular:ci
|
|
21
21
|
ng generate semmet-angular:docker
|
|
22
|
+
ng generate semmet-angular:deploy
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
`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.
|
|
@@ -63,10 +64,28 @@ Por padrão, o schematic não sobrescreve um workflow existente. Use `--force` q
|
|
|
63
64
|
ng generate semmet-angular:docker
|
|
64
65
|
```
|
|
65
66
|
|
|
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
|
|
67
|
+
Esse comando gera `Dockerfile`, `.dockerignore` e `nginx.conf` para empacotar a aplicação Angular como uma imagem servida por Nginx. O Dockerfile usa Node 24 no estágio de build, detecta o gerenciador de pacotes por lockfile, executa o build e copia o output configurado no `angular.json` para `/usr/share/nginx/html`.
|
|
67
68
|
|
|
68
69
|
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
70
|
|
|
71
|
+
## Deploy Guiado
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ng generate semmet-angular:deploy
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
O comando pergunta qual plataforma você quer usar e gera a configuração mínima para o destino escolhido. Também dá para passar a plataforma direto:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ng generate semmet-angular:deploy --platform=vercel
|
|
81
|
+
ng generate semmet-angular:deploy --platform=netlify
|
|
82
|
+
ng generate semmet-angular:deploy --platform=firebase
|
|
83
|
+
ng generate semmet-angular:deploy --platform=github-pages
|
|
84
|
+
ng generate semmet-angular:deploy --platform=docker
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Destinos suportados: Vercel (`vercel.json`), Netlify (`netlify.toml`), Firebase Hosting (`firebase.json` e `.firebaserc`), GitHub Pages (`.github/workflows/deploy.yml`) e deploy baseado em Docker (`compose.yml` e `deploy/docker.README.md`). O schematic detecta o output do build no `angular.json` quando possível e não sobrescreve arquivos existentes sem `--force`.
|
|
88
|
+
|
|
70
89
|
## Testes Gerados
|
|
71
90
|
|
|
72
91
|
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.
|
|
3
|
+
"version": "0.32.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",
|
package/src/collection.json
CHANGED
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"factory": "./docker/index#docker",
|
|
17
17
|
"schema": "./docker/schema.json"
|
|
18
18
|
},
|
|
19
|
+
"deploy": {
|
|
20
|
+
"description": "Generates deploy scaffolding for a selected platform.",
|
|
21
|
+
"factory": "./deploy/index#deploy",
|
|
22
|
+
"schema": "./deploy/schema.json"
|
|
23
|
+
},
|
|
19
24
|
"accordion": {
|
|
20
25
|
"description": "Generates a standalone Angular Accordion component following the W3C ARIA Authoring Practices Guide.",
|
|
21
26
|
"factory": "./accordion/index#accordion",
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploy = deploy;
|
|
4
|
+
const schematics_1 = require("@angular-devkit/schematics");
|
|
5
|
+
function deploy(options) {
|
|
6
|
+
return (tree) => {
|
|
7
|
+
const workspace = readJson(tree, '/angular.json');
|
|
8
|
+
const project = resolveProjectInfo(workspace, options.project);
|
|
9
|
+
const packageManager = detectPackageManager(tree);
|
|
10
|
+
const files = buildDeployFiles(options.platform, project, packageManager);
|
|
11
|
+
const existingFiles = files.map((file) => file.path).filter((path) => tree.exists(path));
|
|
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
|
+
for (const file of files) {
|
|
16
|
+
writeFile(tree, file.path, file.content);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function buildDeployFiles(platform, project, packageManager) {
|
|
21
|
+
if (platform === 'vercel') {
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
path: '/vercel.json',
|
|
25
|
+
content: `${JSON.stringify({
|
|
26
|
+
buildCommand: getBuildCommand(packageManager),
|
|
27
|
+
outputDirectory: project.outputPath,
|
|
28
|
+
rewrites: [{ source: '/(.*)', destination: '/index.html' }],
|
|
29
|
+
}, null, 2)}\n`,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
if (platform === 'netlify') {
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
path: '/netlify.toml',
|
|
37
|
+
content: `[build]
|
|
38
|
+
command = "${getBuildCommand(packageManager)}"
|
|
39
|
+
publish = "${project.outputPath}"
|
|
40
|
+
|
|
41
|
+
[[redirects]]
|
|
42
|
+
from = "/*"
|
|
43
|
+
to = "/index.html"
|
|
44
|
+
status = 200
|
|
45
|
+
`,
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
if (platform === 'firebase') {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
path: '/firebase.json',
|
|
53
|
+
content: `${JSON.stringify({
|
|
54
|
+
hosting: {
|
|
55
|
+
public: project.outputPath,
|
|
56
|
+
ignore: ['firebase.json', '**/.*', '**/node_modules/**'],
|
|
57
|
+
rewrites: [{ source: '**', destination: '/index.html' }],
|
|
58
|
+
},
|
|
59
|
+
}, null, 2)}\n`,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
path: '/.firebaserc',
|
|
63
|
+
content: `${JSON.stringify({ projects: { default: project.name } }, null, 2)}\n`,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
if (platform === 'github-pages') {
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
path: '/.github/workflows/deploy.yml',
|
|
71
|
+
content: buildGitHubPagesWorkflow(project.outputPath, packageManager),
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
path: '/compose.yml',
|
|
78
|
+
content: `services:
|
|
79
|
+
web:
|
|
80
|
+
build: .
|
|
81
|
+
ports:
|
|
82
|
+
- "8080:80"
|
|
83
|
+
restart: unless-stopped
|
|
84
|
+
`,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
path: '/deploy/docker.README.md',
|
|
88
|
+
content: `# Docker Deployment
|
|
89
|
+
|
|
90
|
+
Build and run the generated Docker image:
|
|
91
|
+
|
|
92
|
+
\`\`\`bash
|
|
93
|
+
docker compose up --build
|
|
94
|
+
\`\`\`
|
|
95
|
+
|
|
96
|
+
The application will be available at http://localhost:8080.
|
|
97
|
+
`,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
function buildGitHubPagesWorkflow(outputPath, packageManager) {
|
|
102
|
+
const setupPackageManagerStep = packageManager === 'pnpm'
|
|
103
|
+
? ` - name: Setup pnpm
|
|
104
|
+
uses: pnpm/action-setup@v4
|
|
105
|
+
with:
|
|
106
|
+
version: 9
|
|
107
|
+
|
|
108
|
+
`
|
|
109
|
+
: packageManager === 'yarn'
|
|
110
|
+
? ` - name: Enable Corepack
|
|
111
|
+
run: corepack enable
|
|
112
|
+
|
|
113
|
+
`
|
|
114
|
+
: '';
|
|
115
|
+
return `name: Deploy to GitHub Pages
|
|
116
|
+
|
|
117
|
+
on:
|
|
118
|
+
push:
|
|
119
|
+
branches: [main]
|
|
120
|
+
workflow_dispatch:
|
|
121
|
+
|
|
122
|
+
permissions:
|
|
123
|
+
contents: read
|
|
124
|
+
pages: write
|
|
125
|
+
id-token: write
|
|
126
|
+
|
|
127
|
+
concurrency:
|
|
128
|
+
group: pages
|
|
129
|
+
cancel-in-progress: false
|
|
130
|
+
|
|
131
|
+
jobs:
|
|
132
|
+
build:
|
|
133
|
+
runs-on: ubuntu-latest
|
|
134
|
+
|
|
135
|
+
steps:
|
|
136
|
+
- name: Checkout
|
|
137
|
+
uses: actions/checkout@v4
|
|
138
|
+
|
|
139
|
+
${setupPackageManagerStep} - name: Setup Node
|
|
140
|
+
uses: actions/setup-node@v4
|
|
141
|
+
with:
|
|
142
|
+
node-version: 24
|
|
143
|
+
|
|
144
|
+
- name: Install dependencies
|
|
145
|
+
run: ${getInstallCommand(packageManager)}
|
|
146
|
+
|
|
147
|
+
- name: Build
|
|
148
|
+
run: ${getBuildCommand(packageManager)}
|
|
149
|
+
|
|
150
|
+
- name: Upload artifact
|
|
151
|
+
uses: actions/upload-pages-artifact@v3
|
|
152
|
+
with:
|
|
153
|
+
path: ${outputPath}
|
|
154
|
+
|
|
155
|
+
deploy:
|
|
156
|
+
environment:
|
|
157
|
+
name: github-pages
|
|
158
|
+
url: \${{ steps.deployment.outputs.page_url }}
|
|
159
|
+
runs-on: ubuntu-latest
|
|
160
|
+
needs: build
|
|
161
|
+
|
|
162
|
+
steps:
|
|
163
|
+
- name: Deploy
|
|
164
|
+
id: deployment
|
|
165
|
+
uses: actions/deploy-pages@v4
|
|
166
|
+
`;
|
|
167
|
+
}
|
|
168
|
+
function resolveProjectInfo(workspace, projectName) {
|
|
169
|
+
const name = resolveProjectName(workspace, projectName);
|
|
170
|
+
return {
|
|
171
|
+
name,
|
|
172
|
+
outputPath: resolveBuildOutputPath(workspace, name),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function resolveProjectName(workspace, projectName) {
|
|
176
|
+
if (projectName) {
|
|
177
|
+
if (!workspace.projects?.[projectName]) {
|
|
178
|
+
throw new schematics_1.SchematicsException(`Project "${projectName}" was not found in angular.json.`);
|
|
179
|
+
}
|
|
180
|
+
return projectName;
|
|
181
|
+
}
|
|
182
|
+
if (workspace.defaultProject && workspace.projects?.[workspace.defaultProject]) {
|
|
183
|
+
return workspace.defaultProject;
|
|
184
|
+
}
|
|
185
|
+
const projects = Object.keys(workspace.projects ?? {});
|
|
186
|
+
if (projects.length === 0) {
|
|
187
|
+
throw new schematics_1.SchematicsException('No Angular project was found in angular.json.');
|
|
188
|
+
}
|
|
189
|
+
return projects[0];
|
|
190
|
+
}
|
|
191
|
+
function resolveBuildOutputPath(workspace, projectName) {
|
|
192
|
+
const project = workspace.projects?.[projectName] ?? {};
|
|
193
|
+
const buildTarget = project.architect?.build ?? project.targets?.build;
|
|
194
|
+
const outputPath = buildTarget?.options?.outputPath;
|
|
195
|
+
if (typeof outputPath === 'string') {
|
|
196
|
+
return trimSlashes(outputPath);
|
|
197
|
+
}
|
|
198
|
+
if (outputPath?.base) {
|
|
199
|
+
return trimSlashes([outputPath.base, outputPath.browser].filter(Boolean).join('/'));
|
|
200
|
+
}
|
|
201
|
+
return `dist/${projectName}/browser`;
|
|
202
|
+
}
|
|
203
|
+
function detectPackageManager(tree) {
|
|
204
|
+
if (tree.exists('/pnpm-lock.yaml')) {
|
|
205
|
+
return 'pnpm';
|
|
206
|
+
}
|
|
207
|
+
if (tree.exists('/yarn.lock')) {
|
|
208
|
+
return 'yarn';
|
|
209
|
+
}
|
|
210
|
+
return 'npm';
|
|
211
|
+
}
|
|
212
|
+
function getInstallCommand(packageManager) {
|
|
213
|
+
if (packageManager === 'pnpm') {
|
|
214
|
+
return 'pnpm install --frozen-lockfile';
|
|
215
|
+
}
|
|
216
|
+
if (packageManager === 'yarn') {
|
|
217
|
+
return 'yarn install --frozen-lockfile';
|
|
218
|
+
}
|
|
219
|
+
return 'npm ci';
|
|
220
|
+
}
|
|
221
|
+
function getBuildCommand(packageManager) {
|
|
222
|
+
if (packageManager === 'pnpm') {
|
|
223
|
+
return 'pnpm run build';
|
|
224
|
+
}
|
|
225
|
+
if (packageManager === 'yarn') {
|
|
226
|
+
return 'yarn build';
|
|
227
|
+
}
|
|
228
|
+
return 'npm run build';
|
|
229
|
+
}
|
|
230
|
+
function writeFile(tree, path, content) {
|
|
231
|
+
if (tree.exists(path)) {
|
|
232
|
+
tree.overwrite(path, content);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
tree.create(path, content);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function trimSlashes(value) {
|
|
239
|
+
return value.replace(/^\/+|\/+$/g, '');
|
|
240
|
+
}
|
|
241
|
+
function readJson(tree, path) {
|
|
242
|
+
const content = tree.readText(path);
|
|
243
|
+
return JSON.parse(content);
|
|
244
|
+
}
|
|
245
|
+
//# 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,296 @@
|
|
|
1
|
+
import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { DeployPlatform, Schema } from './schema';
|
|
3
|
+
|
|
4
|
+
type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
5
|
+
|
|
6
|
+
interface DeployFile {
|
|
7
|
+
path: string;
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface ProjectInfo {
|
|
12
|
+
name: string;
|
|
13
|
+
outputPath: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function deploy(options: Schema): Rule {
|
|
17
|
+
return (tree: Tree) => {
|
|
18
|
+
const workspace = readJson(tree, '/angular.json');
|
|
19
|
+
const project = resolveProjectInfo(workspace, options.project);
|
|
20
|
+
const packageManager = detectPackageManager(tree);
|
|
21
|
+
const files = buildDeployFiles(options.platform, project, packageManager);
|
|
22
|
+
const existingFiles = files.map((file) => file.path).filter((path) => tree.exists(path));
|
|
23
|
+
|
|
24
|
+
if (existingFiles.length > 0 && !options.force) {
|
|
25
|
+
throw new SchematicsException(`${existingFiles.join(', ')} already exist. Re-run with --force to overwrite them.`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
writeFile(tree, file.path, file.content);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildDeployFiles(platform: DeployPlatform, project: ProjectInfo, packageManager: PackageManager): DeployFile[] {
|
|
35
|
+
if (platform === 'vercel') {
|
|
36
|
+
return [
|
|
37
|
+
{
|
|
38
|
+
path: '/vercel.json',
|
|
39
|
+
content: `${JSON.stringify(
|
|
40
|
+
{
|
|
41
|
+
buildCommand: getBuildCommand(packageManager),
|
|
42
|
+
outputDirectory: project.outputPath,
|
|
43
|
+
rewrites: [{ source: '/(.*)', destination: '/index.html' }],
|
|
44
|
+
},
|
|
45
|
+
null,
|
|
46
|
+
2
|
|
47
|
+
)}\n`,
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (platform === 'netlify') {
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
path: '/netlify.toml',
|
|
56
|
+
content: `[build]
|
|
57
|
+
command = "${getBuildCommand(packageManager)}"
|
|
58
|
+
publish = "${project.outputPath}"
|
|
59
|
+
|
|
60
|
+
[[redirects]]
|
|
61
|
+
from = "/*"
|
|
62
|
+
to = "/index.html"
|
|
63
|
+
status = 200
|
|
64
|
+
`,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (platform === 'firebase') {
|
|
70
|
+
return [
|
|
71
|
+
{
|
|
72
|
+
path: '/firebase.json',
|
|
73
|
+
content: `${JSON.stringify(
|
|
74
|
+
{
|
|
75
|
+
hosting: {
|
|
76
|
+
public: project.outputPath,
|
|
77
|
+
ignore: ['firebase.json', '**/.*', '**/node_modules/**'],
|
|
78
|
+
rewrites: [{ source: '**', destination: '/index.html' }],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
null,
|
|
82
|
+
2
|
|
83
|
+
)}\n`,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
path: '/.firebaserc',
|
|
87
|
+
content: `${JSON.stringify({ projects: { default: project.name } }, null, 2)}\n`,
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (platform === 'github-pages') {
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
path: '/.github/workflows/deploy.yml',
|
|
96
|
+
content: buildGitHubPagesWorkflow(project.outputPath, packageManager),
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return [
|
|
102
|
+
{
|
|
103
|
+
path: '/compose.yml',
|
|
104
|
+
content: `services:
|
|
105
|
+
web:
|
|
106
|
+
build: .
|
|
107
|
+
ports:
|
|
108
|
+
- "8080:80"
|
|
109
|
+
restart: unless-stopped
|
|
110
|
+
`,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
path: '/deploy/docker.README.md',
|
|
114
|
+
content: `# Docker Deployment
|
|
115
|
+
|
|
116
|
+
Build and run the generated Docker image:
|
|
117
|
+
|
|
118
|
+
\`\`\`bash
|
|
119
|
+
docker compose up --build
|
|
120
|
+
\`\`\`
|
|
121
|
+
|
|
122
|
+
The application will be available at http://localhost:8080.
|
|
123
|
+
`,
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function buildGitHubPagesWorkflow(outputPath: string, packageManager: PackageManager): string {
|
|
129
|
+
const setupPackageManagerStep =
|
|
130
|
+
packageManager === 'pnpm'
|
|
131
|
+
? ` - name: Setup pnpm
|
|
132
|
+
uses: pnpm/action-setup@v4
|
|
133
|
+
with:
|
|
134
|
+
version: 9
|
|
135
|
+
|
|
136
|
+
`
|
|
137
|
+
: packageManager === 'yarn'
|
|
138
|
+
? ` - name: Enable Corepack
|
|
139
|
+
run: corepack enable
|
|
140
|
+
|
|
141
|
+
`
|
|
142
|
+
: '';
|
|
143
|
+
|
|
144
|
+
return `name: Deploy to GitHub Pages
|
|
145
|
+
|
|
146
|
+
on:
|
|
147
|
+
push:
|
|
148
|
+
branches: [main]
|
|
149
|
+
workflow_dispatch:
|
|
150
|
+
|
|
151
|
+
permissions:
|
|
152
|
+
contents: read
|
|
153
|
+
pages: write
|
|
154
|
+
id-token: write
|
|
155
|
+
|
|
156
|
+
concurrency:
|
|
157
|
+
group: pages
|
|
158
|
+
cancel-in-progress: false
|
|
159
|
+
|
|
160
|
+
jobs:
|
|
161
|
+
build:
|
|
162
|
+
runs-on: ubuntu-latest
|
|
163
|
+
|
|
164
|
+
steps:
|
|
165
|
+
- name: Checkout
|
|
166
|
+
uses: actions/checkout@v4
|
|
167
|
+
|
|
168
|
+
${setupPackageManagerStep} - name: Setup Node
|
|
169
|
+
uses: actions/setup-node@v4
|
|
170
|
+
with:
|
|
171
|
+
node-version: 24
|
|
172
|
+
|
|
173
|
+
- name: Install dependencies
|
|
174
|
+
run: ${getInstallCommand(packageManager)}
|
|
175
|
+
|
|
176
|
+
- name: Build
|
|
177
|
+
run: ${getBuildCommand(packageManager)}
|
|
178
|
+
|
|
179
|
+
- name: Upload artifact
|
|
180
|
+
uses: actions/upload-pages-artifact@v3
|
|
181
|
+
with:
|
|
182
|
+
path: ${outputPath}
|
|
183
|
+
|
|
184
|
+
deploy:
|
|
185
|
+
environment:
|
|
186
|
+
name: github-pages
|
|
187
|
+
url: \${{ steps.deployment.outputs.page_url }}
|
|
188
|
+
runs-on: ubuntu-latest
|
|
189
|
+
needs: build
|
|
190
|
+
|
|
191
|
+
steps:
|
|
192
|
+
- name: Deploy
|
|
193
|
+
id: deployment
|
|
194
|
+
uses: actions/deploy-pages@v4
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function resolveProjectInfo(workspace: Record<string, any>, projectName?: string): ProjectInfo {
|
|
199
|
+
const name = resolveProjectName(workspace, projectName);
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
name,
|
|
203
|
+
outputPath: resolveBuildOutputPath(workspace, name),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function resolveProjectName(workspace: Record<string, any>, projectName?: string): string {
|
|
208
|
+
if (projectName) {
|
|
209
|
+
if (!workspace.projects?.[projectName]) {
|
|
210
|
+
throw new SchematicsException(`Project "${projectName}" was not found in angular.json.`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return projectName;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (workspace.defaultProject && workspace.projects?.[workspace.defaultProject]) {
|
|
217
|
+
return workspace.defaultProject as string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const projects = Object.keys(workspace.projects ?? {});
|
|
221
|
+
|
|
222
|
+
if (projects.length === 0) {
|
|
223
|
+
throw new SchematicsException('No Angular project was found in angular.json.');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return projects[0];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function resolveBuildOutputPath(workspace: Record<string, any>, projectName: string): string {
|
|
230
|
+
const project = workspace.projects?.[projectName] ?? {};
|
|
231
|
+
const buildTarget = project.architect?.build ?? project.targets?.build;
|
|
232
|
+
const outputPath = buildTarget?.options?.outputPath;
|
|
233
|
+
|
|
234
|
+
if (typeof outputPath === 'string') {
|
|
235
|
+
return trimSlashes(outputPath);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (outputPath?.base) {
|
|
239
|
+
return trimSlashes([outputPath.base, outputPath.browser].filter(Boolean).join('/'));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return `dist/${projectName}/browser`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function detectPackageManager(tree: Tree): PackageManager {
|
|
246
|
+
if (tree.exists('/pnpm-lock.yaml')) {
|
|
247
|
+
return 'pnpm';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (tree.exists('/yarn.lock')) {
|
|
251
|
+
return 'yarn';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return 'npm';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function getInstallCommand(packageManager: PackageManager): string {
|
|
258
|
+
if (packageManager === 'pnpm') {
|
|
259
|
+
return 'pnpm install --frozen-lockfile';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (packageManager === 'yarn') {
|
|
263
|
+
return 'yarn install --frozen-lockfile';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return 'npm ci';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function getBuildCommand(packageManager: PackageManager): string {
|
|
270
|
+
if (packageManager === 'pnpm') {
|
|
271
|
+
return 'pnpm run build';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (packageManager === 'yarn') {
|
|
275
|
+
return 'yarn build';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return 'npm run build';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function writeFile(tree: Tree, path: string, content: string): void {
|
|
282
|
+
if (tree.exists(path)) {
|
|
283
|
+
tree.overwrite(path, content);
|
|
284
|
+
} else {
|
|
285
|
+
tree.create(path, content);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function trimSlashes(value: string): string {
|
|
290
|
+
return value.replace(/^\/+|\/+$/g, '');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function readJson(tree: Tree, path: string): Record<string, any> {
|
|
294
|
+
const content = tree.readText(path);
|
|
295
|
+
return JSON.parse(content) as Record<string, any>;
|
|
296
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["schema.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "SemmetAngularDeploy",
|
|
4
|
+
"title": "Semmet Angular Deploy Options Schema",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"description": "Generates deploy scaffolding for a selected platform.",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"platform": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"enum": ["vercel", "netlify", "firebase", "github-pages", "docker"],
|
|
12
|
+
"description": "The deployment platform to scaffold.",
|
|
13
|
+
"x-prompt": {
|
|
14
|
+
"message": "Which deployment platform do you want to target?",
|
|
15
|
+
"type": "list",
|
|
16
|
+
"items": [
|
|
17
|
+
{ "value": "vercel", "label": "Vercel" },
|
|
18
|
+
{ "value": "netlify", "label": "Netlify" },
|
|
19
|
+
{ "value": "firebase", "label": "Firebase Hosting" },
|
|
20
|
+
{ "value": "github-pages", "label": "GitHub Pages" },
|
|
21
|
+
{ "value": "docker", "label": "Docker-based deployment" }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"force": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"default": false,
|
|
28
|
+
"description": "Overwrite existing deployment files."
|
|
29
|
+
},
|
|
30
|
+
"project": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "The Angular project to deploy.",
|
|
33
|
+
"$default": { "$source": "projectName" }
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"required": ["platform"]
|
|
37
|
+
}
|
package/src/docker/index.js
CHANGED
|
@@ -81,7 +81,7 @@ function getPackageManagerCommands(packageManager, hasNpmLockfile) {
|
|
|
81
81
|
}
|
|
82
82
|
function buildDockerfile(outputPath, commands) {
|
|
83
83
|
const prepareStep = commands.prepare ? `${commands.prepare}\n` : '';
|
|
84
|
-
return `FROM node:
|
|
84
|
+
return `FROM node:24-alpine AS build
|
|
85
85
|
WORKDIR /app
|
|
86
86
|
|
|
87
87
|
COPY package*.json ./
|
package/src/docker/index.ts
CHANGED
|
@@ -110,7 +110,7 @@ function getPackageManagerCommands(packageManager: PackageManager, hasNpmLockfil
|
|
|
110
110
|
function buildDockerfile(outputPath: string, commands: PackageManagerCommands): string {
|
|
111
111
|
const prepareStep = commands.prepare ? `${commands.prepare}\n` : '';
|
|
112
112
|
|
|
113
|
-
return `FROM node:
|
|
113
|
+
return `FROM node:24-alpine AS build
|
|
114
114
|
WORKDIR /app
|
|
115
115
|
|
|
116
116
|
COPY package*.json ./
|