front-end-dev-standards 1.0.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.md +167 -0
- package/config/default.json +8 -0
- package/package.json +40 -0
- package/scripts/npm-cmd.js +25 -0
- package/scripts/setup.js +280 -0
- package/scripts/validate-package.js +41 -0
- package/standards/angular.md +234 -0
- package/standards/architecture.md +263 -0
- package/standards/coding-style.md +223 -0
- package/standards/security.md +6 -0
- package/standards/testing.md +244 -0
- package/templates/company.mdc.template +92 -0
- package/templates/copilot-instructions.md.template +92 -0
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# front-end-dev-standards
|
|
2
|
+
|
|
3
|
+
Company-wide Angular coding standards packaged for **Cursor AI** and **GitHub Copilot**.
|
|
4
|
+
|
|
5
|
+
## What It Does
|
|
6
|
+
|
|
7
|
+
When installed in an Angular project, the postinstall script automatically:
|
|
8
|
+
|
|
9
|
+
1. Creates `.ai-standards/` with standards markdown files
|
|
10
|
+
2. Creates `.cursor/rules/company.mdc` for Cursor
|
|
11
|
+
3. Creates `.github/copilot-instructions.md` for GitHub Copilot
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
npm install -D front-end-dev-standards
|
|
15
|
+
↓
|
|
16
|
+
postinstall runs setup.js
|
|
17
|
+
↓
|
|
18
|
+
.ai-standards/ ← standards source of truth (local copy)
|
|
19
|
+
.cursor/rules/ ← Cursor reads this automatically
|
|
20
|
+
.github/ ← Copilot reads copilot-instructions.md
|
|
21
|
+
↓
|
|
22
|
+
AI tools generate standardized Angular code
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### From npm registry (production)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install -D front-end-dev-standards
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Local development (this monorepo)
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Already wired via file: dependency in root package.json
|
|
37
|
+
npm install
|
|
38
|
+
npm run standards:setup
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Manual Commands
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Idempotent setup (skips existing files)
|
|
45
|
+
npm run standards:setup
|
|
46
|
+
|
|
47
|
+
# Force overwrite all generated files
|
|
48
|
+
npm run standards:setup:force
|
|
49
|
+
|
|
50
|
+
# Preview changes without writing
|
|
51
|
+
node packages/dev-standards/scripts/setup.js --dry-run
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
Create `.standardsrc.json` in your project root:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"overwrite": false,
|
|
61
|
+
"targets": {
|
|
62
|
+
"standardsDir": ".ai-standards",
|
|
63
|
+
"cursorRulesFile": ".cursor/rules/company.mdc",
|
|
64
|
+
"copilotInstructionsFile": ".github/copilot-instructions.md"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Option | Default | Description |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| `overwrite` | `false` | When `true`, replaces existing generated files |
|
|
72
|
+
| `targets.standardsDir` | `.ai-standards` | Where standards MD files are copied |
|
|
73
|
+
| `targets.cursorRulesFile` | `.cursor/rules/company.mdc` | Cursor rules output path |
|
|
74
|
+
| `targets.copilotInstructionsFile` | `.github/copilot-instructions.md` | Copilot instructions path |
|
|
75
|
+
|
|
76
|
+
CLI flag `--force` overrides `overwrite` to `true` for a single run.
|
|
77
|
+
|
|
78
|
+
## Adding a New Standards File
|
|
79
|
+
|
|
80
|
+
1. Create any `*.md` file in `packages/dev-standards/standards/` (e.g. `security.md`)
|
|
81
|
+
2. Run `npm run standards:setup` (or `npm install` — postinstall runs automatically)
|
|
82
|
+
|
|
83
|
+
All `.md` files in `standards/` are **auto-discovered** — no need to edit `config/default.json`.
|
|
84
|
+
|
|
85
|
+
When a new file is added, Cursor and Copilot config files are refreshed automatically.
|
|
86
|
+
To update **existing** standards content, run `npm run standards:setup:force`.
|
|
87
|
+
|
|
88
|
+
## Standards Files
|
|
89
|
+
|
|
90
|
+
| File | Purpose |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `standards/angular.md` | Angular 20+ patterns: standalone, signals, inject, forms |
|
|
93
|
+
| `standards/architecture.md` | Feature-based folder structure, layers, data flow |
|
|
94
|
+
| `standards/coding-style.md` | TypeScript naming, formatting, review checklist |
|
|
95
|
+
| `standards/testing.md` | Vitest conventions, coverage expectations |
|
|
96
|
+
|
|
97
|
+
## How Cursor Reads Rules
|
|
98
|
+
|
|
99
|
+
Cursor automatically loads `.cursor/rules/*.mdc` files from your project.
|
|
100
|
+
|
|
101
|
+
The generated `company.mdc` has `alwaysApply: true`, so every AI interaction in the project follows your standards.
|
|
102
|
+
|
|
103
|
+
## How GitHub Copilot Reads Standards
|
|
104
|
+
|
|
105
|
+
Copilot reads `.github/copilot-instructions.md` at the repository root.
|
|
106
|
+
|
|
107
|
+
It does **not** read `.cursor/rules/` — that's why the postinstall script generates a separate Copilot file from the same standards.
|
|
108
|
+
|
|
109
|
+
## Versioning & Upgrades
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npm update front-end-dev-standards
|
|
113
|
+
npm run standards:setup:force # refresh generated files
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Publishing as npm Package
|
|
117
|
+
|
|
118
|
+
Registry: **https://registry.npmjs.org/** (configured in `.npmrc` and `publishConfig`)
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# From repo root
|
|
122
|
+
npm login
|
|
123
|
+
npm run standards:validate
|
|
124
|
+
npm run standards:pack
|
|
125
|
+
npm run standards:publish
|
|
126
|
+
|
|
127
|
+
# Version bumps + publish
|
|
128
|
+
npm run standards:publish:patch
|
|
129
|
+
npm run standards:publish:minor
|
|
130
|
+
npm run standards:publish:major
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Consumer projects install with:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"devDependencies": {
|
|
138
|
+
"front-end-dev-standards": "^1.0.0"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The package's own `postinstall` hook runs `setup.js` automatically on `npm install`.
|
|
144
|
+
|
|
145
|
+
## Package Structure
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
packages/dev-standards/
|
|
149
|
+
├── config/
|
|
150
|
+
│ └── default.json
|
|
151
|
+
├── scripts/
|
|
152
|
+
│ └── setup.js
|
|
153
|
+
├── standards/
|
|
154
|
+
│ ├── angular.md
|
|
155
|
+
│ ├── architecture.md
|
|
156
|
+
│ ├── coding-style.md
|
|
157
|
+
│ └── testing.md
|
|
158
|
+
├── templates/
|
|
159
|
+
│ ├── company.mdc.template
|
|
160
|
+
│ └── copilot-instructions.md.template
|
|
161
|
+
├── package.json
|
|
162
|
+
└── README.md
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
UNLICENSED — internal company use only.
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "front-end-dev-standards",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Company-wide Angular coding standards for Cursor AI and GitHub Copilot",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./scripts/setup.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"front-end-dev-standards-setup": "./scripts/setup.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"standards/",
|
|
13
|
+
"templates/",
|
|
14
|
+
"scripts/",
|
|
15
|
+
"config/",
|
|
16
|
+
"README.md",
|
|
17
|
+
".npmrc"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"postinstall": "node scripts/setup.js",
|
|
21
|
+
"setup": "node scripts/setup.js",
|
|
22
|
+
"setup:force": "node scripts/setup.js --force",
|
|
23
|
+
"prepublishOnly": "node scripts/validate-package.js",
|
|
24
|
+
"pack:dry-run": "npm pack --dry-run"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"registry": "https://registry.npmjs.org/",
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"angular",
|
|
35
|
+
"standards",
|
|
36
|
+
"cursor",
|
|
37
|
+
"copilot",
|
|
38
|
+
"ai"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Run npm commands from the dev-standards package directory.
|
|
5
|
+
* Ensures pack/publish only includes package files (not the Angular workspace root).
|
|
6
|
+
*
|
|
7
|
+
* Usage (from repo root):
|
|
8
|
+
* node packages/dev-standards/scripts/npm-cmd.js pack --ignore-scripts
|
|
9
|
+
* node packages/dev-standards/scripts/npm-cmd.js publish --ignore-scripts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execSync } from 'node:child_process';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
const npmArgs = process.argv.slice(2);
|
|
18
|
+
|
|
19
|
+
if (npmArgs.length === 0) {
|
|
20
|
+
console.error('[dev-standards] Usage: npm-cmd.js <npm-args...>');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
process.chdir(PACKAGE_ROOT);
|
|
25
|
+
execSync(`npm ${npmArgs.join(' ')}`, { stdio: 'inherit', shell: true });
|
package/scripts/setup.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* dev-standards — postinstall setup script
|
|
5
|
+
*
|
|
6
|
+
* Copies company standards into the consuming Angular project and generates
|
|
7
|
+
* AI tool configuration files for Cursor and GitHub Copilot.
|
|
8
|
+
*
|
|
9
|
+
* Idempotent by default: existing files are skipped unless --force is passed
|
|
10
|
+
* or .standardsrc.json sets "overwrite": true.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node scripts/setup.js # safe, idempotent install
|
|
14
|
+
* node scripts/setup.js --force # overwrite all generated files
|
|
15
|
+
* node scripts/setup.js --dry-run # preview without writing
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
|
|
22
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
23
|
+
const __dirname = path.dirname(__filename);
|
|
24
|
+
|
|
25
|
+
/** Directory where this package lives (packages/dev-standards or node_modules/dev-standards) */
|
|
26
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
27
|
+
|
|
28
|
+
/** Parse CLI flags */
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
const FORCE = args.includes('--force');
|
|
31
|
+
const DRY_RUN = args.includes('--dry-run');
|
|
32
|
+
const VERBOSE = args.includes('--verbose') || args.includes('-v');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the consuming project root.
|
|
36
|
+
* Walks up from cwd until it finds a package.json that is NOT this standards package.
|
|
37
|
+
*/
|
|
38
|
+
function findProjectRoot(startDir = process.cwd()) {
|
|
39
|
+
let current = path.resolve(startDir);
|
|
40
|
+
|
|
41
|
+
while (current !== path.dirname(current)) {
|
|
42
|
+
const pkgPath = path.join(current, 'package.json');
|
|
43
|
+
if (fs.existsSync(pkgPath)) {
|
|
44
|
+
try {
|
|
45
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
46
|
+
// If this package.json belongs to dev-standards, keep walking up
|
|
47
|
+
if (pkg.name === 'front-end-dev-standards' || pkg.name === 'dev-standards') {
|
|
48
|
+
current = path.dirname(current);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
return current;
|
|
52
|
+
} catch {
|
|
53
|
+
// malformed package.json — keep searching
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
current = path.dirname(current);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return process.cwd();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Load merged configuration: package defaults + project .standardsrc.json
|
|
64
|
+
*/
|
|
65
|
+
function loadConfig(projectRoot) {
|
|
66
|
+
const defaultsPath = path.join(PACKAGE_ROOT, 'config', 'default.json');
|
|
67
|
+
const defaults = JSON.parse(fs.readFileSync(defaultsPath, 'utf8'));
|
|
68
|
+
|
|
69
|
+
const rcPath = path.join(projectRoot, '.standardsrc.json');
|
|
70
|
+
if (!fs.existsSync(rcPath)) {
|
|
71
|
+
return { ...defaults, overwrite: FORCE || defaults.overwrite };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const rc = JSON.parse(fs.readFileSync(rcPath, 'utf8'));
|
|
75
|
+
return {
|
|
76
|
+
...defaults,
|
|
77
|
+
...rc,
|
|
78
|
+
targets: { ...defaults.targets, ...rc.targets },
|
|
79
|
+
overwrite: FORCE || rc.overwrite === true || defaults.overwrite === true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolve which standards files to copy.
|
|
85
|
+
* Default: all *.md files in packages/dev-standards/standards/ (auto-discovered).
|
|
86
|
+
* Optional: set "standardsFiles" in .standardsrc.json to copy a specific subset only.
|
|
87
|
+
*/
|
|
88
|
+
function resolveStandardsFiles(sourceStandardsDir, config) {
|
|
89
|
+
if (Array.isArray(config.standardsFiles) && config.standardsFiles.length > 0) {
|
|
90
|
+
return config.standardsFiles;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!fs.existsSync(sourceStandardsDir)) {
|
|
94
|
+
warn(`Standards directory not found: ${sourceStandardsDir}`);
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return fs
|
|
99
|
+
.readdirSync(sourceStandardsDir)
|
|
100
|
+
.filter((file) => file.endsWith('.md') && fs.statSync(path.join(sourceStandardsDir, file)).isFile())
|
|
101
|
+
.sort();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Ensure directory exists */
|
|
105
|
+
function ensureDir(dirPath) {
|
|
106
|
+
if (!fs.existsSync(dirPath)) {
|
|
107
|
+
if (DRY_RUN) {
|
|
108
|
+
log(`[dry-run] mkdir ${dirPath}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
112
|
+
log(`Created directory: ${rel(dirPath)}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Copy file if missing or overwrite enabled */
|
|
117
|
+
function copyFile(src, dest, overwrite) {
|
|
118
|
+
const destExists = fs.existsSync(dest);
|
|
119
|
+
|
|
120
|
+
if (destExists && !overwrite) {
|
|
121
|
+
log(`Skipped (exists): ${rel(dest)}`);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (DRY_RUN) {
|
|
126
|
+
log(`[dry-run] copy ${rel(src)} → ${rel(dest)}${destExists ? ' (overwrite)' : ''}`);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
ensureDir(path.dirname(dest));
|
|
131
|
+
fs.copyFileSync(src, dest);
|
|
132
|
+
log(`${destExists ? 'Updated' : 'Created'}: ${rel(dest)}`);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Generate file from template with token replacement */
|
|
137
|
+
function generateFromTemplate(templatePath, destPath, tokens, overwrite) {
|
|
138
|
+
if (!fs.existsSync(templatePath)) {
|
|
139
|
+
warn(`Template not found: ${rel(templatePath)}`);
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const destExists = fs.existsSync(destPath);
|
|
144
|
+
if (destExists && !overwrite) {
|
|
145
|
+
log(`Skipped (exists): ${rel(destPath)}`);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let content = fs.readFileSync(templatePath, 'utf8');
|
|
150
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
151
|
+
content = content.replaceAll(`{{${key}}}`, value);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (DRY_RUN) {
|
|
155
|
+
log(`[dry-run] generate ${rel(destPath)}${destExists ? ' (overwrite)' : ''}`);
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
ensureDir(path.dirname(destPath));
|
|
160
|
+
fs.writeFileSync(destPath, content, 'utf8');
|
|
161
|
+
log(`${destExists ? 'Updated' : 'Created'}: ${rel(destPath)}`);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Paths relative to project root for readable logs */
|
|
166
|
+
let projectRoot = process.cwd();
|
|
167
|
+
function rel(absPath) {
|
|
168
|
+
return path.relative(projectRoot, absPath) || absPath;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function log(message) {
|
|
172
|
+
console.log(`[dev-standards] ${message}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function warn(message) {
|
|
176
|
+
console.warn(`[dev-standards] WARN: ${message}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Main setup routine
|
|
181
|
+
*/
|
|
182
|
+
function run() {
|
|
183
|
+
projectRoot = findProjectRoot();
|
|
184
|
+
const config = loadConfig(projectRoot);
|
|
185
|
+
const overwrite = config.overwrite === true;
|
|
186
|
+
|
|
187
|
+
log(`Project root: ${projectRoot}`);
|
|
188
|
+
log(`Mode: ${DRY_RUN ? 'dry-run' : overwrite ? 'overwrite' : 'idempotent'}`);
|
|
189
|
+
|
|
190
|
+
const standardsDir = path.join(projectRoot, config.targets.standardsDir);
|
|
191
|
+
const cursorRulesFile = path.join(projectRoot, config.targets.cursorRulesFile);
|
|
192
|
+
const copilotFile = path.join(projectRoot, config.targets.copilotInstructionsFile);
|
|
193
|
+
|
|
194
|
+
ensureDir(standardsDir);
|
|
195
|
+
ensureDir(path.dirname(cursorRulesFile));
|
|
196
|
+
ensureDir(path.dirname(copilotFile));
|
|
197
|
+
|
|
198
|
+
// 1. Copy standards markdown files (auto-discover all *.md in standards/)
|
|
199
|
+
const sourceStandardsDir = path.join(PACKAGE_ROOT, 'standards');
|
|
200
|
+
const standardsFiles = resolveStandardsFiles(sourceStandardsDir, config);
|
|
201
|
+
|
|
202
|
+
if (standardsFiles.length === 0) {
|
|
203
|
+
warn('No standards .md files found to copy.');
|
|
204
|
+
} else if (VERBOSE) {
|
|
205
|
+
log(`Standards files: ${standardsFiles.join(', ')}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let copiedCount = 0;
|
|
209
|
+
let anyNewStandard = false;
|
|
210
|
+
|
|
211
|
+
for (const file of standardsFiles) {
|
|
212
|
+
const src = path.join(sourceStandardsDir, file);
|
|
213
|
+
const dest = path.join(standardsDir, file);
|
|
214
|
+
const destExists = fs.existsSync(dest);
|
|
215
|
+
|
|
216
|
+
if (!fs.existsSync(src)) {
|
|
217
|
+
warn(`Standard file missing in package: ${file}`);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (copyFile(src, dest, overwrite)) {
|
|
222
|
+
copiedCount++;
|
|
223
|
+
if (!destExists) {
|
|
224
|
+
anyNewStandard = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 2. Generate Cursor rules (.mdc) — refresh when new standards are added or --force
|
|
230
|
+
const standardsRelativePaths = standardsFiles.map((f) =>
|
|
231
|
+
path.join(config.targets.standardsDir, f).replace(/\\/g, '/'),
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
const templateTokens = {
|
|
235
|
+
STANDARDS_VERSION: readPackageVersion(),
|
|
236
|
+
STANDARDS_PATHS: standardsRelativePaths.map((p) => `- \`${p}\``).join('\n'),
|
|
237
|
+
GENERATED_AT: new Date().toISOString(),
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const regenAiConfig = overwrite || anyNewStandard || !fs.existsSync(cursorRulesFile);
|
|
241
|
+
|
|
242
|
+
generateFromTemplate(
|
|
243
|
+
path.join(PACKAGE_ROOT, 'templates', 'company.mdc.template'),
|
|
244
|
+
cursorRulesFile,
|
|
245
|
+
templateTokens,
|
|
246
|
+
regenAiConfig,
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// 3. Generate GitHub Copilot instructions
|
|
250
|
+
generateFromTemplate(
|
|
251
|
+
path.join(PACKAGE_ROOT, 'templates', 'copilot-instructions.md.template'),
|
|
252
|
+
copilotFile,
|
|
253
|
+
templateTokens,
|
|
254
|
+
regenAiConfig,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
log(`Setup complete. ${copiedCount} standard file(s) processed.`);
|
|
258
|
+
log(`Cursor rules: ${rel(cursorRulesFile)}`);
|
|
259
|
+
log(`Copilot instructions: ${rel(copilotFile)}`);
|
|
260
|
+
|
|
261
|
+
if (!overwrite && !anyNewStandard) {
|
|
262
|
+
log('Tip: run with --force or set "overwrite": true in .standardsrc.json to refresh existing standards files.');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function readPackageVersion() {
|
|
267
|
+
try {
|
|
268
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
269
|
+
return pkg.version ?? 'unknown';
|
|
270
|
+
} catch {
|
|
271
|
+
return 'unknown';
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
run();
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error('[dev-standards] Setup failed:', error.message);
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pre-publish validation — ensures required package files exist before npm publish.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
12
|
+
|
|
13
|
+
const REQUIRED_PATHS = [
|
|
14
|
+
'package.json',
|
|
15
|
+
'README.md',
|
|
16
|
+
'scripts/setup.js',
|
|
17
|
+
'config/default.json',
|
|
18
|
+
'templates/company.mdc.template',
|
|
19
|
+
'templates/copilot-instructions.md.template',
|
|
20
|
+
'standards/angular.md',
|
|
21
|
+
'standards/architecture.md',
|
|
22
|
+
'standards/coding-style.md',
|
|
23
|
+
'standards/testing.md',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const missing = REQUIRED_PATHS.filter((rel) => !fs.existsSync(path.join(PACKAGE_ROOT, rel)));
|
|
27
|
+
|
|
28
|
+
if (missing.length > 0) {
|
|
29
|
+
console.error('[dev-standards] Publish validation failed. Missing files:');
|
|
30
|
+
missing.forEach((file) => console.error(` - ${file}`));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf8'));
|
|
35
|
+
|
|
36
|
+
if (pkg.private === true) {
|
|
37
|
+
console.error('[dev-standards] Publish validation failed: "private": true must be removed from package.json');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log(`[dev-standards] Publish validation passed (v${pkg.version}).`);
|