@rtorcato/repo-tooling 2.59.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/AGENTS.md +113 -0
- package/LICENSE +21 -0
- package/README.md +201 -0
- package/dist/cli/commands/doctor.js +336 -0
- package/dist/cli/commands/fix-targets.js +192 -0
- package/dist/cli/commands/fix.js +440 -0
- package/dist/cli/commands/setup-presets.js +281 -0
- package/dist/cli/commands/setup.js +501 -0
- package/dist/cli/generators/agent-rules.js +103 -0
- package/dist/cli/generators/badges.js +88 -0
- package/dist/cli/generators/build.js +216 -0
- package/dist/cli/generators/bun.js +25 -0
- package/dist/cli/generators/community-health.js +145 -0
- package/dist/cli/generators/docs-site.js +436 -0
- package/dist/cli/generators/git.js +164 -0
- package/dist/cli/generators/github-actions.js +35 -0
- package/dist/cli/generators/github-workflows.js +24 -0
- package/dist/cli/generators/gitlab-ci.js +10 -0
- package/dist/cli/generators/index.js +123 -0
- package/dist/cli/generators/linting.js +54 -0
- package/dist/cli/generators/misc.js +298 -0
- package/dist/cli/generators/nx.js +16 -0
- package/dist/cli/generators/package-json.js +317 -0
- package/dist/cli/generators/pnpm-workspace.js +126 -0
- package/dist/cli/generators/postcss.js +24 -0
- package/dist/cli/generators/readme.js +268 -0
- package/dist/cli/generators/security.js +156 -0
- package/dist/cli/generators/skills-install.js +70 -0
- package/dist/cli/generators/tailwind.js +34 -0
- package/dist/cli/generators/testing.js +88 -0
- package/dist/cli/generators/treeshake.js +148 -0
- package/dist/cli/generators/tsconfig.js +27 -0
- package/dist/cli/generators/turborepo.js +35 -0
- package/dist/cli/generators/typedoc.js +40 -0
- package/dist/cli/index.js +332 -0
- package/dist/cli/utils/copy-preset.js +98 -0
- package/dist/cli/utils/detect-language.js +22 -0
- package/dist/cli/utils/format.js +32 -0
- package/dist/cli/utils/install.js +28 -0
- package/dist/cli/utils/lockfile.js +84 -0
- package/dist/languages/js/checks.js +949 -0
- package/dist/languages/js/ci.js +251 -0
- package/dist/languages/js/fixers.js +735 -0
- package/dist/languages/registry.js +37 -0
- package/dist/languages/swift/checks.js +127 -0
- package/dist/languages/swift/ci.js +165 -0
- package/dist/languages/swift/fixers.js +102 -0
- package/dist/languages/swift/git-hooks.js +63 -0
- package/dist/languages/swift/gitignore.js +43 -0
- package/dist/languages/swift/scaffold.js +244 -0
- package/package.json +461 -0
- package/tooling/biome/README.md +90 -0
- package/tooling/biome/biome.json +63 -0
- package/tooling/bun/bunfig.toml +14 -0
- package/tooling/changesets/README.md +35 -0
- package/tooling/changesets/config.json +11 -0
- package/tooling/claude/repo-tooling.md +87 -0
- package/tooling/commitlint/commitlint.d.mts +4 -0
- package/tooling/commitlint/commitlint.mjs +40 -0
- package/tooling/cypress/cypress.config.d.mts +4 -0
- package/tooling/cypress/cypress.config.mjs +11 -0
- package/tooling/docusaurus/index.d.mts +17 -0
- package/tooling/docusaurus/index.mjs +38 -0
- package/tooling/docusaurus/sync-changelog.mjs +42 -0
- package/tooling/docusaurus/theme-tokens.css +79 -0
- package/tooling/docusaurus/theme.css +378 -0
- package/tooling/esbuild/index.d.mts +6 -0
- package/tooling/esbuild/index.mjs +102 -0
- package/tooling/eslint/base.d.mts +6 -0
- package/tooling/eslint/base.mjs +122 -0
- package/tooling/eslint/nextjs.d.mts +4 -0
- package/tooling/eslint/nextjs.mjs +22 -0
- package/tooling/eslint/types.d.ts +58 -0
- package/tooling/github-actions/workflows/cloudflare-pages.yml +42 -0
- package/tooling/github-actions/workflows/docker-publish.yml +44 -0
- package/tooling/github-actions/workflows/preview-deployments.yml +54 -0
- package/tooling/github-actions/workflows/vercel-deploy.yml +40 -0
- package/tooling/jest-presets/browser/jest-preset.d.mts +4 -0
- package/tooling/jest-presets/browser/jest-preset.mjs +14 -0
- package/tooling/jest-presets/node/jest-preset.d.mts +4 -0
- package/tooling/jest-presets/node/jest-preset.mjs +13 -0
- package/tooling/mcp/mcp.json.example +27 -0
- package/tooling/nx/nx.json +24 -0
- package/tooling/oxlint/README.md +25 -0
- package/tooling/oxlint/oxlintrc.json +28 -0
- package/tooling/playwright/playwright.config.d.mts +4 -0
- package/tooling/playwright/playwright.config.mjs +19 -0
- package/tooling/prettier/index.d.mts +4 -0
- package/tooling/prettier/index.mjs +36 -0
- package/tooling/release-please/.release-please-manifest.json +3 -0
- package/tooling/release-please/release-please-config.json +9 -0
- package/tooling/rolldown/rolldown.config.d.mts +18 -0
- package/tooling/rolldown/rolldown.config.mjs +59 -0
- package/tooling/rollup/rollup.config.d.mts +20 -0
- package/tooling/rollup/rollup.config.mjs +70 -0
- package/tooling/semantic-release/docker.d.mts +4 -0
- package/tooling/semantic-release/docker.mjs +59 -0
- package/tooling/semantic-release/github.d.mts +4 -0
- package/tooling/semantic-release/github.mjs +79 -0
- package/tooling/semantic-release/index.d.mts +4 -0
- package/tooling/semantic-release/index.mjs +80 -0
- package/tooling/swift/periphery.yml +4 -0
- package/tooling/swift/swiftlint.yml +23 -0
- package/tooling/tests/exports-resolution.d.mts +14 -0
- package/tooling/tests/exports-resolution.mjs +68 -0
- package/tooling/tests/ssr-safety.d.mts +14 -0
- package/tooling/tests/ssr-safety.mjs +53 -0
- package/tooling/tsup/index.d.mts +8 -0
- package/tooling/tsup/index.mjs +33 -0
- package/tooling/typedoc/typedoc.json +7 -0
- package/tooling/typescript/README.md +49 -0
- package/tooling/typescript/reset.d.ts +9 -0
- package/tooling/typescript/tsconfig.base.json +84 -0
- package/tooling/typescript/tsconfig.build.json +11 -0
- package/tooling/typescript/tsconfig.bun.json +9 -0
- package/tooling/typescript/tsconfig.express.json +9 -0
- package/tooling/typescript/tsconfig.next.json +20 -0
- package/tooling/typescript/tsconfig.node.json +9 -0
- package/tooling/typescript/tsconfig.react.json +15 -0
- package/tooling/typescript/tsconfig.test.json +8 -0
- package/tooling/typescript/v1/tsconfig.base.json +81 -0
- package/tooling/typescript/v1/tsconfig.express.json +9 -0
- package/tooling/typescript/v1/tsconfig.next.json +19 -0
- package/tooling/typescript/v1/tsconfig.node.json +9 -0
- package/tooling/typescript/v1/tsconfig.react.json +14 -0
- package/tooling/typescript/v1/tsconfig.test.json +8 -0
- package/tooling/vite/vite.config.d.mts +4 -0
- package/tooling/vite/vite.config.mjs +18 -0
- package/tooling/vitest/jsdom-shims.d.mts +1 -0
- package/tooling/vitest/jsdom-shims.mjs +58 -0
- package/tooling/vitest/vitest.config.d.mts +4 -0
- package/tooling/vitest/vitest.config.mjs +25 -0
- package/tooling/vitest/vitest.config.react.d.mts +4 -0
- package/tooling/vitest/vitest.config.react.mjs +27 -0
- package/tooling/vitest/vitest.setup.d.mts +1 -0
- package/tooling/vitest/vitest.setup.mjs +3 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Shared badge-block builder. All URLs are derived from package.json (name +
|
|
2
|
+
// repository) — no hardcoded owner/repo — so it works for any consumer.
|
|
3
|
+
export const BADGE_START = '<!-- js-tooling:badges:start -->';
|
|
4
|
+
export const BADGE_END = '<!-- js-tooling:badges:end -->';
|
|
5
|
+
/**
|
|
6
|
+
* Parse a package.json `repository` field into `{ owner, repo }`.
|
|
7
|
+
* Handles the shorthand (`owner/repo`, `github:owner/repo`) and full git URLs
|
|
8
|
+
* (`git+https://github.com/owner/repo.git`, `git@github.com:owner/repo.git`).
|
|
9
|
+
* Returns null for non-GitHub or unparseable values.
|
|
10
|
+
*/
|
|
11
|
+
export function parseRepository(repository) {
|
|
12
|
+
const url = typeof repository === 'string'
|
|
13
|
+
? repository
|
|
14
|
+
: repository && typeof repository === 'object'
|
|
15
|
+
? repository.url
|
|
16
|
+
: undefined;
|
|
17
|
+
if (typeof url !== 'string' || url.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
// `owner/repo` or `github:owner/repo` shorthand (no scheme, no host).
|
|
20
|
+
if (!url.includes('://') && !url.includes('@')) {
|
|
21
|
+
const short = url.replace(/^github:/, '').match(/^([\w.-]+)\/([\w.-]+?)(?:\.git)?$/);
|
|
22
|
+
if (short?.[1] && short[2])
|
|
23
|
+
return { owner: short[1], repo: short[2] };
|
|
24
|
+
}
|
|
25
|
+
const m = url.match(/github\.com[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:$|[/#?])/);
|
|
26
|
+
return m?.[1] && m[2] ? { owner: m[1], repo: m[2] } : null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build the badge markdown row (no delimiter comments). Returns '' when only
|
|
30
|
+
* the always-on license badge would render — i.e. nothing package- or
|
|
31
|
+
* repo-specific is derivable, so a badge row isn't worth adding.
|
|
32
|
+
*/
|
|
33
|
+
export function buildBadgeRow(inputs) {
|
|
34
|
+
const { name, owner, repo, branch = 'main', isPrivate = false } = inputs;
|
|
35
|
+
const slug = owner && repo ? `${owner}/${repo}` : null;
|
|
36
|
+
const badges = [];
|
|
37
|
+
let specific = false;
|
|
38
|
+
if (slug) {
|
|
39
|
+
specific = true;
|
|
40
|
+
badges.push(`[](https://github.com/${slug}/actions/workflows/ci.yml)`);
|
|
41
|
+
}
|
|
42
|
+
if (!isPrivate && name) {
|
|
43
|
+
specific = true;
|
|
44
|
+
badges.push(`[](https://www.npmjs.com/package/${name})`);
|
|
45
|
+
badges.push(`[](https://www.npmjs.com/package/${name})`);
|
|
46
|
+
badges.push(`[](https://bundlephobia.com/package/${name})`);
|
|
47
|
+
}
|
|
48
|
+
if (!isPrivate && slug) {
|
|
49
|
+
badges.push(`[](https://codecov.io/gh/${slug})`);
|
|
50
|
+
}
|
|
51
|
+
badges.push('[](https://opensource.org/licenses/MIT)');
|
|
52
|
+
return specific ? badges.join('\n') : '';
|
|
53
|
+
}
|
|
54
|
+
/** Build the full delimited badge block, or '' when there's nothing worth showing. */
|
|
55
|
+
export function buildBadgeBlock(inputs) {
|
|
56
|
+
const row = buildBadgeRow(inputs);
|
|
57
|
+
return row ? `${BADGE_START}\n${row}\n${BADGE_END}` : '';
|
|
58
|
+
}
|
|
59
|
+
/** True when the text carries npm/bundlephobia/codecov badges (which 404 on private/unpublished). */
|
|
60
|
+
export function hasPublicOnlyBadges(text) {
|
|
61
|
+
return /img\.shields\.io\/npm\/|bundlephobia|codecov\.io/.test(text);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Insert or refresh the badge block in a README. Replaces an existing delimited
|
|
65
|
+
* block in place; otherwise inserts right after the first H1 (or prepends when
|
|
66
|
+
* there is none). Passing an empty block strips any existing one. Idempotent.
|
|
67
|
+
*/
|
|
68
|
+
export function upsertBadges(readme, block) {
|
|
69
|
+
const start = readme.indexOf(BADGE_START);
|
|
70
|
+
const end = readme.indexOf(BADGE_END);
|
|
71
|
+
const hasBlock = start !== -1 && end !== -1 && end > start;
|
|
72
|
+
if (!block) {
|
|
73
|
+
if (!hasBlock)
|
|
74
|
+
return readme;
|
|
75
|
+
// Strip the block plus the blank line that follows it, if any.
|
|
76
|
+
const after = readme.slice(end + BADGE_END.length).replace(/^\n\n/, '\n');
|
|
77
|
+
return (readme.slice(0, start).replace(/\n\n$/, '\n') + after).replace(/^\n+/, '');
|
|
78
|
+
}
|
|
79
|
+
if (hasBlock) {
|
|
80
|
+
return readme.slice(0, start) + block + readme.slice(end + BADGE_END.length);
|
|
81
|
+
}
|
|
82
|
+
const lines = readme.split('\n');
|
|
83
|
+
const h1 = lines.findIndex((l) => /^#\s+/.test(l));
|
|
84
|
+
if (h1 === -1)
|
|
85
|
+
return `${block}\n\n${readme}`;
|
|
86
|
+
lines.splice(h1 + 1, 0, '', block);
|
|
87
|
+
return lines.join('\n');
|
|
88
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
// tsup, esbuild, and vite all pull in esbuild, whose install-time build script
|
|
4
|
+
// pnpm 11 refuses to run until it's approved — otherwise `pnpm install` fails
|
|
5
|
+
// with ERR_PNPM_IGNORED_BUILDS.
|
|
6
|
+
export function bundlerNeedsEsbuild(config) {
|
|
7
|
+
return config.bundler === 'tsup' || config.bundler === 'esbuild' || config.bundler === 'vite';
|
|
8
|
+
}
|
|
9
|
+
// pnpm 11 reads build-script approvals from the `allowBuilds` map (package →
|
|
10
|
+
// boolean), NOT the older `onlyBuiltDependencies` list — verified against the
|
|
11
|
+
// pinned pnpm@11.1.3, which ignores the list form and errors with
|
|
12
|
+
// ERR_PNPM_IGNORED_BUILDS.
|
|
13
|
+
const SINGLE_PACKAGE_BUILD_APPROVALS = `allowBuilds:
|
|
14
|
+
esbuild: true
|
|
15
|
+
`;
|
|
16
|
+
/**
|
|
17
|
+
* pnpm 11 no longer reads build-script approvals from package.json's `pnpm`
|
|
18
|
+
* field — they live in pnpm-workspace.yaml. For a single-package esbuild-backed
|
|
19
|
+
* build, write a minimal pnpm-workspace.yaml approving esbuild. Never clobbers
|
|
20
|
+
* an existing file (the treeshake-check path writes a richer one that already
|
|
21
|
+
* lists esbuild). Must run after that path so its file wins. Returns the
|
|
22
|
+
* relative path if written, else null.
|
|
23
|
+
*/
|
|
24
|
+
export async function ensureBuildApprovals(config, targetDir) {
|
|
25
|
+
if (!bundlerNeedsEsbuild(config))
|
|
26
|
+
return null;
|
|
27
|
+
const wsPath = path.join(targetDir, 'pnpm-workspace.yaml');
|
|
28
|
+
if (await fs.pathExists(wsPath))
|
|
29
|
+
return null;
|
|
30
|
+
await fs.writeFile(wsPath, SINGLE_PACKAGE_BUILD_APPROVALS);
|
|
31
|
+
return 'pnpm-workspace.yaml';
|
|
32
|
+
}
|
|
33
|
+
export async function generateBuildConfigs(config, targetDir) {
|
|
34
|
+
if (config.bundler === 'tsup') {
|
|
35
|
+
await generateTsupConfig(targetDir);
|
|
36
|
+
}
|
|
37
|
+
else if (config.bundler === 'esbuild') {
|
|
38
|
+
await generateEsbuildConfig(targetDir);
|
|
39
|
+
}
|
|
40
|
+
else if (config.bundler === 'rollup') {
|
|
41
|
+
await generateRollupConfig(targetDir);
|
|
42
|
+
}
|
|
43
|
+
else if (config.bundler === 'rolldown') {
|
|
44
|
+
await generateRolldownConfig(targetDir);
|
|
45
|
+
}
|
|
46
|
+
else if (config.bundler === 'vite') {
|
|
47
|
+
await generateViteConfig(config, targetDir);
|
|
48
|
+
}
|
|
49
|
+
// Generate semantic-release config for GitHub
|
|
50
|
+
if (config.semanticRelease) {
|
|
51
|
+
await generateSemanticReleaseConfig(targetDir);
|
|
52
|
+
}
|
|
53
|
+
// Generate Changesets config (alternative to semantic-release)
|
|
54
|
+
if (config.changesets) {
|
|
55
|
+
await generateChangesetsConfig(targetDir);
|
|
56
|
+
}
|
|
57
|
+
// Generate Release Please config (alternative to semantic-release)
|
|
58
|
+
if (config.releasePlease) {
|
|
59
|
+
await generateReleasePleaseConfig(targetDir);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function generateTsupConfig(targetDir) {
|
|
63
|
+
const tsupConfigPath = path.join(targetDir, 'tsup.config.ts');
|
|
64
|
+
const tsupConfig = `import { getConfig } from '@rtorcato/repo-tooling/tsup'
|
|
65
|
+
|
|
66
|
+
export default getConfig({
|
|
67
|
+
entry: ['src/index.ts'],
|
|
68
|
+
format: ['cjs', 'esm'],
|
|
69
|
+
dts: true,
|
|
70
|
+
clean: true,
|
|
71
|
+
splitting: false,
|
|
72
|
+
sourcemap: true,
|
|
73
|
+
}, process.env.NODE_ENV || 'development')
|
|
74
|
+
`;
|
|
75
|
+
await fs.writeFile(tsupConfigPath, tsupConfig);
|
|
76
|
+
}
|
|
77
|
+
async function generateEsbuildConfig(targetDir) {
|
|
78
|
+
const esbuildConfigPath = path.join(targetDir, 'build.mjs');
|
|
79
|
+
const esbuildConfig = `import { build } from 'esbuild'
|
|
80
|
+
import { nodeExternalsPlugin } from 'esbuild-node-externals'
|
|
81
|
+
|
|
82
|
+
const isProduction = process.env.NODE_ENV === 'production'
|
|
83
|
+
|
|
84
|
+
await build({
|
|
85
|
+
entryPoints: ['src/index.ts'],
|
|
86
|
+
bundle: true,
|
|
87
|
+
outdir: 'dist',
|
|
88
|
+
format: 'esm',
|
|
89
|
+
target: 'node18',
|
|
90
|
+
platform: 'node',
|
|
91
|
+
minify: isProduction,
|
|
92
|
+
sourcemap: !isProduction,
|
|
93
|
+
plugins: [nodeExternalsPlugin()],
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
console.log('Build completed!')
|
|
97
|
+
`;
|
|
98
|
+
await fs.writeFile(esbuildConfigPath, esbuildConfig);
|
|
99
|
+
}
|
|
100
|
+
export async function generateRollupConfig(targetDir) {
|
|
101
|
+
const rollupConfigPath = path.join(targetDir, 'rollup.config.mjs');
|
|
102
|
+
const rollupConfig = `export { default } from '@rtorcato/repo-tooling/rollup'
|
|
103
|
+
`;
|
|
104
|
+
await fs.writeFile(rollupConfigPath, rollupConfig);
|
|
105
|
+
}
|
|
106
|
+
export async function generateRolldownConfig(targetDir) {
|
|
107
|
+
const rolldownConfigPath = path.join(targetDir, 'rolldown.config.mjs');
|
|
108
|
+
const rolldownConfig = `export { default } from '@rtorcato/repo-tooling/rolldown'
|
|
109
|
+
`;
|
|
110
|
+
await fs.writeFile(rolldownConfigPath, rolldownConfig);
|
|
111
|
+
}
|
|
112
|
+
export async function generateViteConfig(config, targetDir) {
|
|
113
|
+
const viteConfigPath = path.join(targetDir, 'vite.config.ts');
|
|
114
|
+
// React apps need the plugin; we layer it on top of the shipped preset.
|
|
115
|
+
const viteConfig = config.projectType === 'react-app'
|
|
116
|
+
? `import preset from '@rtorcato/repo-tooling/vite'
|
|
117
|
+
import react from '@vitejs/plugin-react'
|
|
118
|
+
import { defineConfig, mergeConfig } from 'vite'
|
|
119
|
+
|
|
120
|
+
export default mergeConfig(preset, defineConfig({ plugins: [react()] }))
|
|
121
|
+
`
|
|
122
|
+
: `export { default } from '@rtorcato/repo-tooling/vite'
|
|
123
|
+
`;
|
|
124
|
+
await fs.writeFile(viteConfigPath, viteConfig);
|
|
125
|
+
}
|
|
126
|
+
// Plugins the github/gitlab preset activates that semantic-release core does
|
|
127
|
+
// NOT bundle (core bundles only commit-analyzer, release-notes-generator, npm,
|
|
128
|
+
// github). Without these in the consumer's deps, `semantic-release` crashes
|
|
129
|
+
// with "Cannot find module '@semantic-release/changelog'" on first run.
|
|
130
|
+
const RELEASE_PLUGIN_DEPS = {
|
|
131
|
+
'@semantic-release/changelog': '^6.0.0',
|
|
132
|
+
'@semantic-release/git': '^10.0.0',
|
|
133
|
+
};
|
|
134
|
+
export async function generateSemanticReleaseConfig(targetDir) {
|
|
135
|
+
const releaseConfigPath = path.join(targetDir, 'release.config.mjs');
|
|
136
|
+
const releaseConfig = `export { default } from '@rtorcato/repo-tooling/semantic-release/github'
|
|
137
|
+
`;
|
|
138
|
+
await fs.writeFile(releaseConfigPath, releaseConfig);
|
|
139
|
+
const written = ['release.config.mjs'];
|
|
140
|
+
// Ensure the preset's non-bundled plugins are installed; otherwise the
|
|
141
|
+
// scaffolded release.config.mjs references modules the consumer lacks.
|
|
142
|
+
const pkgPath = path.join(targetDir, 'package.json');
|
|
143
|
+
if (await fs.pathExists(pkgPath)) {
|
|
144
|
+
const pkg = (await fs.readJson(pkgPath));
|
|
145
|
+
const devDeps = (pkg.devDependencies ?? {});
|
|
146
|
+
const deps = (pkg.dependencies ?? {});
|
|
147
|
+
let changed = false;
|
|
148
|
+
for (const [name, version] of Object.entries(RELEASE_PLUGIN_DEPS)) {
|
|
149
|
+
if (!devDeps[name] && !deps[name]) {
|
|
150
|
+
devDeps[name] = version;
|
|
151
|
+
changed = true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (changed) {
|
|
155
|
+
pkg.devDependencies = devDeps;
|
|
156
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
157
|
+
written.push('package.json');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return written;
|
|
161
|
+
}
|
|
162
|
+
export async function generateChangesetsConfig(targetDir) {
|
|
163
|
+
// Drop the canonical Changesets config into .changeset/config.json. The user
|
|
164
|
+
// owns this file once it's in their repo; subsequent `pnpm changeset` runs
|
|
165
|
+
// create per-change markdown files alongside it.
|
|
166
|
+
const { copyPreset } = await import('../utils/copy-preset.js');
|
|
167
|
+
await copyPreset('changesets', targetDir);
|
|
168
|
+
}
|
|
169
|
+
// The release-please workflow — googleapis/release-please-action opens/maintains
|
|
170
|
+
// a release PR on every push to main and tags + creates the GitHub release when
|
|
171
|
+
// it merges. RELEASE_TOKEN (falling back to GITHUB_TOKEN) lets the release PR's
|
|
172
|
+
// checks run, mirroring the semantic-release setup.
|
|
173
|
+
const RELEASE_PLEASE_WORKFLOW = `name: release-please
|
|
174
|
+
|
|
175
|
+
on:
|
|
176
|
+
push:
|
|
177
|
+
branches: [main]
|
|
178
|
+
|
|
179
|
+
permissions:
|
|
180
|
+
contents: write
|
|
181
|
+
pull-requests: write
|
|
182
|
+
|
|
183
|
+
jobs:
|
|
184
|
+
release-please:
|
|
185
|
+
runs-on: ubuntu-latest
|
|
186
|
+
steps:
|
|
187
|
+
- uses: googleapis/release-please-action@v4
|
|
188
|
+
with:
|
|
189
|
+
token: \${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
|
|
190
|
+
config-file: release-please-config.json
|
|
191
|
+
manifest-file: .release-please-manifest.json
|
|
192
|
+
`;
|
|
193
|
+
/**
|
|
194
|
+
* Scaffolds Release Please: the config (from the shipped preset), a starting
|
|
195
|
+
* manifest, and the release workflow. The config is (re)written to realign with
|
|
196
|
+
* the preset, but the manifest (holds live versions) and the workflow (may be
|
|
197
|
+
* user-tuned) are only created when absent. Returns the relative paths touched.
|
|
198
|
+
*/
|
|
199
|
+
export async function generateReleasePleaseConfig(targetDir) {
|
|
200
|
+
const { copyPreset, getPackageRoot } = await import('../utils/copy-preset.js');
|
|
201
|
+
const written = [];
|
|
202
|
+
const cfg = await copyPreset('release-please', targetDir);
|
|
203
|
+
written.push(cfg.target);
|
|
204
|
+
const manifestDest = path.join(targetDir, '.release-please-manifest.json');
|
|
205
|
+
if (!(await fs.pathExists(manifestDest))) {
|
|
206
|
+
await fs.copy(path.join(getPackageRoot(), 'tooling/release-please/.release-please-manifest.json'), manifestDest);
|
|
207
|
+
}
|
|
208
|
+
written.push('.release-please-manifest.json');
|
|
209
|
+
const workflowPath = path.join(targetDir, '.github', 'workflows', 'release-please.yml');
|
|
210
|
+
if (!(await fs.pathExists(workflowPath))) {
|
|
211
|
+
await fs.ensureDir(path.dirname(workflowPath));
|
|
212
|
+
await fs.writeFile(workflowPath, RELEASE_PLEASE_WORKFLOW);
|
|
213
|
+
}
|
|
214
|
+
written.push('.github/workflows/release-please.yml');
|
|
215
|
+
return written;
|
|
216
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
/**
|
|
4
|
+
* Scaffold Bun toolchain files: a bunfig.toml and — only when no tsconfig.json
|
|
5
|
+
* exists yet — a tsconfig.json that extends the Bun-typed preset
|
|
6
|
+
* (`@rtorcato/repo-tooling/typescript/bun`, which adds `types: ['bun']`). Both are
|
|
7
|
+
* safe-add: an existing bunfig.toml or tsconfig.json is never clobbered (an
|
|
8
|
+
* existing project points its tsconfig at the bun preset by hand — see the docs).
|
|
9
|
+
* Returns the relative paths written.
|
|
10
|
+
*/
|
|
11
|
+
export async function generateBun(targetDir) {
|
|
12
|
+
const written = [];
|
|
13
|
+
const bunfigPath = path.join(targetDir, 'bunfig.toml');
|
|
14
|
+
if (!(await fs.pathExists(bunfigPath))) {
|
|
15
|
+
const { copyPreset } = await import('../utils/copy-preset.js');
|
|
16
|
+
const result = await copyPreset('bun', targetDir);
|
|
17
|
+
written.push(result.target);
|
|
18
|
+
}
|
|
19
|
+
const tsconfigPath = path.join(targetDir, 'tsconfig.json');
|
|
20
|
+
if (!(await fs.pathExists(tsconfigPath))) {
|
|
21
|
+
await fs.writeJson(tsconfigPath, { extends: '@rtorcato/repo-tooling/typescript/bun' }, { spaces: 2 });
|
|
22
|
+
written.push('tsconfig.json');
|
|
23
|
+
}
|
|
24
|
+
return written;
|
|
25
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
const CONTRIBUTING = `# Contributing
|
|
4
|
+
|
|
5
|
+
Thanks for your interest in contributing!
|
|
6
|
+
|
|
7
|
+
## Local setup
|
|
8
|
+
|
|
9
|
+
\`\`\`bash
|
|
10
|
+
pnpm install
|
|
11
|
+
pnpm build
|
|
12
|
+
pnpm test
|
|
13
|
+
\`\`\`
|
|
14
|
+
|
|
15
|
+
## Commit messages
|
|
16
|
+
|
|
17
|
+
This project uses [Conventional Commits](https://www.conventionalcommits.org/).
|
|
18
|
+
Format: \`type(scope): summary\` — e.g. \`fix(api): handle empty payload\`.
|
|
19
|
+
Common types: \`feat\`, \`fix\`, \`docs\`, \`refactor\`, \`test\`, \`chore\`.
|
|
20
|
+
If commitizen is set up, run \`pnpm commit\` to be guided through it.
|
|
21
|
+
|
|
22
|
+
## Pull requests
|
|
23
|
+
|
|
24
|
+
- Keep PRs focused and small where possible.
|
|
25
|
+
- Make sure \`pnpm test\` and \`pnpm check\` (lint) pass.
|
|
26
|
+
- Add tests for new behaviour.
|
|
27
|
+
- Describe what changed and why in the PR description.
|
|
28
|
+
`;
|
|
29
|
+
// Relative advisory link works from a repo-root SECURITY.md on GitHub, so the
|
|
30
|
+
// template stays repo-agnostic (no hardcoded owner/name). Fill in the contact.
|
|
31
|
+
const SECURITY = `# Security Policy
|
|
32
|
+
|
|
33
|
+
## Supported versions
|
|
34
|
+
|
|
35
|
+
Only the latest major version receives security fixes.
|
|
36
|
+
|
|
37
|
+
## Reporting a vulnerability
|
|
38
|
+
|
|
39
|
+
Do **not** open a public GitHub issue for security vulnerabilities.
|
|
40
|
+
|
|
41
|
+
Instead, use [GitHub's private vulnerability reporting](../../security/advisories/new)
|
|
42
|
+
or email **<security-contact@example.com>** with:
|
|
43
|
+
|
|
44
|
+
- A description of the vulnerability
|
|
45
|
+
- Steps to reproduce
|
|
46
|
+
- Potential impact
|
|
47
|
+
|
|
48
|
+
You'll receive a response within 5 business days.
|
|
49
|
+
`;
|
|
50
|
+
const PULL_REQUEST_TEMPLATE = `## Summary
|
|
51
|
+
|
|
52
|
+
<!-- 1-3 bullet points describing what changed and why -->
|
|
53
|
+
-
|
|
54
|
+
|
|
55
|
+
## Type of change
|
|
56
|
+
|
|
57
|
+
- [ ] Bug fix
|
|
58
|
+
- [ ] New feature
|
|
59
|
+
- [ ] Refactor / cleanup
|
|
60
|
+
- [ ] Documentation
|
|
61
|
+
- [ ] CI / tooling
|
|
62
|
+
|
|
63
|
+
## Test plan
|
|
64
|
+
|
|
65
|
+
- [ ] Existing tests pass (\`pnpm test\`)
|
|
66
|
+
- [ ] New tests added for new behaviour
|
|
67
|
+
|
|
68
|
+
## Checklist
|
|
69
|
+
|
|
70
|
+
- [ ] No debug logging left in production code
|
|
71
|
+
- [ ] No breaking changes (or BREAKING CHANGE footer added to commit)
|
|
72
|
+
- [ ] Lint passes (\`pnpm check\` / \`pnpm lint\`)
|
|
73
|
+
`;
|
|
74
|
+
const BUG_REPORT = `---
|
|
75
|
+
name: Bug report
|
|
76
|
+
about: Something isn't working as expected
|
|
77
|
+
labels: bug
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Describe the bug
|
|
81
|
+
|
|
82
|
+
<!-- A clear and concise description of what the bug is -->
|
|
83
|
+
|
|
84
|
+
## Steps to reproduce
|
|
85
|
+
|
|
86
|
+
1.
|
|
87
|
+
2.
|
|
88
|
+
3.
|
|
89
|
+
|
|
90
|
+
## Expected behaviour
|
|
91
|
+
|
|
92
|
+
## Actual behaviour
|
|
93
|
+
|
|
94
|
+
<!-- Paste any error output here -->
|
|
95
|
+
|
|
96
|
+
\`\`\`
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
## Environment
|
|
100
|
+
|
|
101
|
+
- Package version:
|
|
102
|
+
- Node version (\`node -v\`):
|
|
103
|
+
- Package manager + version:
|
|
104
|
+
- OS:
|
|
105
|
+
`;
|
|
106
|
+
const FEATURE_REQUEST = `---
|
|
107
|
+
name: Feature request
|
|
108
|
+
about: Suggest an idea or improvement
|
|
109
|
+
labels: enhancement
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Problem
|
|
113
|
+
|
|
114
|
+
<!-- What are you trying to do that you can't do today? -->
|
|
115
|
+
|
|
116
|
+
## Proposed solution
|
|
117
|
+
|
|
118
|
+
## Alternatives considered
|
|
119
|
+
|
|
120
|
+
## Additional context
|
|
121
|
+
`;
|
|
122
|
+
const FILES = [
|
|
123
|
+
{ rel: 'CONTRIBUTING.md', content: CONTRIBUTING },
|
|
124
|
+
{ rel: 'SECURITY.md', content: SECURITY },
|
|
125
|
+
{ rel: '.github/PULL_REQUEST_TEMPLATE.md', content: PULL_REQUEST_TEMPLATE },
|
|
126
|
+
{ rel: '.github/ISSUE_TEMPLATE/bug_report.md', content: BUG_REPORT },
|
|
127
|
+
{ rel: '.github/ISSUE_TEMPLATE/feature_request.md', content: FEATURE_REQUEST },
|
|
128
|
+
];
|
|
129
|
+
/**
|
|
130
|
+
* Scaffold GitHub community-health files. Safe-add: existing files are left
|
|
131
|
+
* untouched (the user owns them once written). Returns the list of files
|
|
132
|
+
* actually created.
|
|
133
|
+
*/
|
|
134
|
+
export async function generateCommunityHealth(targetDir) {
|
|
135
|
+
const written = [];
|
|
136
|
+
for (const { rel, content } of FILES) {
|
|
137
|
+
const filepath = path.join(targetDir, rel);
|
|
138
|
+
if (await fs.pathExists(filepath))
|
|
139
|
+
continue;
|
|
140
|
+
await fs.ensureDir(path.dirname(filepath));
|
|
141
|
+
await fs.writeFile(filepath, content);
|
|
142
|
+
written.push(rel);
|
|
143
|
+
}
|
|
144
|
+
return written;
|
|
145
|
+
}
|