@coryrylan/tools 0.1.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/LICENSE +21 -0
- package/README.md +193 -0
- package/dist/eslint/configs/browser.d.ts +12 -0
- package/dist/eslint/configs/browser.js +40 -0
- package/dist/eslint/configs/html.d.ts +10 -0
- package/dist/eslint/configs/html.js +38 -0
- package/dist/eslint/configs/json.d.ts +13 -0
- package/dist/eslint/configs/json.js +39 -0
- package/dist/eslint/configs/shared.d.ts +11 -0
- package/dist/eslint/configs/shared.js +19 -0
- package/dist/eslint/configs/tests.d.ts +8 -0
- package/dist/eslint/configs/tests.js +20 -0
- package/dist/eslint/configs/typescript.d.ts +18 -0
- package/dist/eslint/configs/typescript.js +104 -0
- package/dist/eslint/index.d.ts +27 -0
- package/dist/eslint/index.js +19 -0
- package/dist/eslint/plugin.d.ts +8 -0
- package/dist/eslint/plugin.js +36 -0
- package/dist/eslint/rules/consistent-error-messages.d.ts +10 -0
- package/dist/eslint/rules/consistent-error-messages.js +127 -0
- package/dist/eslint/rules/no-dead-code.d.ts +16 -0
- package/dist/eslint/rules/no-dead-code.js +110 -0
- package/dist/eslint/rules/no-deep-class-inheritance.d.ts +19 -0
- package/dist/eslint/rules/no-deep-class-inheritance.js +132 -0
- package/dist/eslint/rules/no-reexport-barrels.d.ts +10 -0
- package/dist/eslint/rules/no-reexport-barrels.js +55 -0
- package/dist/eslint/rules/no-single-consumer-abstraction.d.ts +3 -0
- package/dist/eslint/rules/no-single-consumer-abstraction.js +364 -0
- package/dist/eslint/rules/no-unjustified-disable.d.ts +11 -0
- package/dist/eslint/rules/no-unjustified-disable.js +75 -0
- package/dist/eslint/rules/no-unpinned-dependency-ranges.d.ts +46 -0
- package/dist/eslint/rules/no-unpinned-dependency-ranges.js +79 -0
- package/dist/eslint/rules/require-listener-cleanup.d.ts +8 -0
- package/dist/eslint/rules/require-listener-cleanup.js +202 -0
- package/dist/eslint/rules/require-observer-cleanup.d.ts +8 -0
- package/dist/eslint/rules/require-observer-cleanup.js +69 -0
- package/dist/eslint/rules/require-timer-cleanup.d.ts +10 -0
- package/dist/eslint/rules/require-timer-cleanup.js +142 -0
- package/dist/eslint/rules/utils.d.ts +45 -0
- package/dist/eslint/rules/utils.js +109 -0
- package/dist/prettier/index.d.ts +15 -0
- package/dist/prettier/index.js +26 -0
- package/dist/stylelint/index.d.ts +10 -0
- package/dist/stylelint/index.js +50 -0
- package/dist/vale/styles/config/vocabularies/Tools/accept.txt +80 -0
- package/dist/vale/styles/config/vocabularies/Tools/reject.txt +5 -0
- package/dist/vale/vale.ini +27 -0
- package/dist/vite/index.d.ts +37 -0
- package/dist/vite/index.js +61 -0
- package/dist/vite/plugins/dts.d.ts +26 -0
- package/dist/vite/plugins/dts.js +106 -0
- package/dist/vite/plugins/write-if-changed.d.ts +20 -0
- package/dist/vite/plugins/write-if-changed.js +33 -0
- package/dist/vitest/browser.d.ts +31 -0
- package/dist/vitest/browser.js +105 -0
- package/dist/vitest/index.d.ts +28 -0
- package/dist/vitest/index.js +34 -0
- package/package.json +200 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/stylelint/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Personal Stylelint preferences layered on `stylelint-config-standard`.
|
|
4
|
+
* Point a `stylelint.config.js` at it with
|
|
5
|
+
* `export { default } from '@coryrylan/tools/stylelint';`, or import the
|
|
6
|
+
* default export and add `rules`/`overrides` on top for project-specific
|
|
7
|
+
* exceptions.
|
|
8
|
+
*/
|
|
9
|
+
var config = {
|
|
10
|
+
extends: ["stylelint-config-standard"],
|
|
11
|
+
ignoreFiles: [
|
|
12
|
+
"**/node_modules/**",
|
|
13
|
+
"**/dist/**",
|
|
14
|
+
"**/.wireit/**",
|
|
15
|
+
"**/.eslintcache/**",
|
|
16
|
+
"**/coverage/**"
|
|
17
|
+
],
|
|
18
|
+
rules: {
|
|
19
|
+
"alpha-value-notation": "number",
|
|
20
|
+
"hue-degree-notation": "number",
|
|
21
|
+
"property-no-vendor-prefix": null,
|
|
22
|
+
"declaration-empty-line-before": null,
|
|
23
|
+
"custom-property-empty-line-before": null,
|
|
24
|
+
"import-notation": null,
|
|
25
|
+
"custom-property-pattern": "^_?[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
|
26
|
+
"value-keyword-case": ["lower", { ignoreKeywords: ["currentColor"] }],
|
|
27
|
+
"declaration-block-no-redundant-longhand-properties": [true, { ignoreShorthands: ["grid-template"] }],
|
|
28
|
+
"no-duplicate-selectors": null,
|
|
29
|
+
"no-descending-specificity": null,
|
|
30
|
+
"comment-empty-line-before": null,
|
|
31
|
+
"property-disallowed-list": [
|
|
32
|
+
"margin-left",
|
|
33
|
+
"margin-right",
|
|
34
|
+
"margin-top",
|
|
35
|
+
"margin-bottom",
|
|
36
|
+
"padding-left",
|
|
37
|
+
"padding-right",
|
|
38
|
+
"padding-top",
|
|
39
|
+
"padding-bottom"
|
|
40
|
+
],
|
|
41
|
+
"declaration-property-value-disallowed-list": [{
|
|
42
|
+
"/^(margin|padding)(-(top|right|bottom|left|inline|block|inline-start|inline-end|block-start|block-end))?$/": ["/(?<!\\w)\\d+px/"],
|
|
43
|
+
"/^(gap|row-gap|column-gap)$/": ["/(?<!\\w)\\d+px/"],
|
|
44
|
+
"/^(top|right|bottom|left)$/": ["/(?<!\\w)\\d+px/"],
|
|
45
|
+
"/^inset(-(inline|block)(-(start|end))?)?$/": ["/(?<!\\w)\\d+px/"]
|
|
46
|
+
}, { message: "Use a CSS custom property (design token) instead of a hardcoded pixel value." }]
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
export { config as default };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
(H|h)ardcoded
|
|
2
|
+
a11y
|
|
3
|
+
allowlist(s)?
|
|
4
|
+
API(s)?
|
|
5
|
+
ARIA
|
|
6
|
+
blocklist(s)?
|
|
7
|
+
boolean(s)?
|
|
8
|
+
bundler(s)?
|
|
9
|
+
changelog
|
|
10
|
+
CLI
|
|
11
|
+
codebase(s)?
|
|
12
|
+
config(s)?
|
|
13
|
+
CSS
|
|
14
|
+
declaratively
|
|
15
|
+
dedupe(d|s)?
|
|
16
|
+
deduplicate(d|s)?
|
|
17
|
+
deduplication
|
|
18
|
+
dev
|
|
19
|
+
devDependenc(y|ies)
|
|
20
|
+
DOM
|
|
21
|
+
downlevel
|
|
22
|
+
Eleventy
|
|
23
|
+
ESLint
|
|
24
|
+
ESM
|
|
25
|
+
extensionless
|
|
26
|
+
flexbox
|
|
27
|
+
frontmatter
|
|
28
|
+
GitHub
|
|
29
|
+
globals
|
|
30
|
+
greppable
|
|
31
|
+
HTML
|
|
32
|
+
i18n
|
|
33
|
+
IDE
|
|
34
|
+
ini
|
|
35
|
+
inlined
|
|
36
|
+
istanbul
|
|
37
|
+
JSDoc
|
|
38
|
+
JSON(C)?
|
|
39
|
+
linter(s)?
|
|
40
|
+
linting
|
|
41
|
+
lockfile
|
|
42
|
+
longhand(s)?
|
|
43
|
+
lookup(s)?
|
|
44
|
+
MCP
|
|
45
|
+
middleware
|
|
46
|
+
mise
|
|
47
|
+
mixin(s)?
|
|
48
|
+
monorepo(s)?
|
|
49
|
+
multiline
|
|
50
|
+
namespace(d|s)?
|
|
51
|
+
npm
|
|
52
|
+
NVIDIA
|
|
53
|
+
parens
|
|
54
|
+
Playwright
|
|
55
|
+
pluggable
|
|
56
|
+
pnpm
|
|
57
|
+
polyfill(s)?
|
|
58
|
+
pre-release
|
|
59
|
+
Prettier
|
|
60
|
+
repo(s)?
|
|
61
|
+
Rolldown
|
|
62
|
+
Rollup('s)?
|
|
63
|
+
Rylan
|
|
64
|
+
semver
|
|
65
|
+
SSR
|
|
66
|
+
Stylelint
|
|
67
|
+
stylesheet(s)?
|
|
68
|
+
subclass(ers|es|ing)?
|
|
69
|
+
superclass('s|es)?
|
|
70
|
+
teardown(s)?
|
|
71
|
+
toolchain(s)?
|
|
72
|
+
tsconfig
|
|
73
|
+
TypeScript
|
|
74
|
+
typescript-eslint
|
|
75
|
+
uncomment(ed)?
|
|
76
|
+
Vale
|
|
77
|
+
Vite
|
|
78
|
+
Vitest
|
|
79
|
+
wireit
|
|
80
|
+
write-good
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# StylesPath assumes a consumer's .vale.ini lives at the project root, so
|
|
2
|
+
# node_modules/@coryrylan/tools resolves relative to it - adjust the path if
|
|
3
|
+
# this template is copied elsewhere. Run `vale sync` once after installing to
|
|
4
|
+
# download the Google and write-good packages into StylesPath.
|
|
5
|
+
StylesPath = node_modules/@coryrylan/tools/dist/vale/styles
|
|
6
|
+
MinAlertLevel = warning
|
|
7
|
+
Vocab = Tools
|
|
8
|
+
Packages = Google, write-good
|
|
9
|
+
|
|
10
|
+
[*.md]
|
|
11
|
+
BasedOnStyles = Vale, Google, write-good
|
|
12
|
+
TokenIgnores = (\{%[\s\S]*?%\}), (\{\{[^}]*\}\})
|
|
13
|
+
Vale.Terms = NO
|
|
14
|
+
Google.Headings = NO
|
|
15
|
+
Google.WordList = NO
|
|
16
|
+
; Spaced em dashes, `e.g.`/`i.e.`, and logical quote punctuation
|
|
17
|
+
; (periods outside quotation marks) are the house style.
|
|
18
|
+
Google.EmDash = NO
|
|
19
|
+
Google.Latin = NO
|
|
20
|
+
Google.Quotes = NO
|
|
21
|
+
|
|
22
|
+
[*.ts]
|
|
23
|
+
BasedOnStyles = Vale, write-good
|
|
24
|
+
write-good.TooWordy = error
|
|
25
|
+
write-good.Passive = error
|
|
26
|
+
Vale.Spelling = NO
|
|
27
|
+
Vale.Terms = NO
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LibraryOptions, UserConfig } from 'vite';
|
|
2
|
+
/** Options for {@link createNodeLibraryBuildConfig}. */
|
|
3
|
+
export interface NodeLibraryBuildConfigOptions {
|
|
4
|
+
readonly entry: LibraryOptions['entry'];
|
|
5
|
+
}
|
|
6
|
+
/** Options for {@link createBrowserLibraryBuildConfig}. */
|
|
7
|
+
export interface BrowserLibraryBuildConfigOptions {
|
|
8
|
+
readonly entry: LibraryOptions['entry'];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Vite build config for a Node.js library entry point: ESM output only,
|
|
12
|
+
* targets `node22`, and never bundles a dependency - every bare specifier
|
|
13
|
+
* (`lit`, `node:path`, `@scope/pkg`) is externalized. `preserveModules`
|
|
14
|
+
* keeps one output file per source module instead of one monolithic
|
|
15
|
+
* bundle, so `package.json#exports` can expose deep-import paths.
|
|
16
|
+
*
|
|
17
|
+
* Extend it with `mergeConfig` rather than hand-rolling the shape again:
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { mergeConfig } from 'vite';
|
|
21
|
+
* import { createNodeLibraryBuildConfig } from '@coryrylan/tools/vite';
|
|
22
|
+
*
|
|
23
|
+
* export default mergeConfig(
|
|
24
|
+
* createNodeLibraryBuildConfig({ entry: { index: 'src/index.ts' } }),
|
|
25
|
+
* { plugins: [] },
|
|
26
|
+
* );
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function createNodeLibraryBuildConfig(options: NodeLibraryBuildConfigOptions): UserConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Vite build config for a browser library entry point. Same shape as
|
|
32
|
+
* {@link createNodeLibraryBuildConfig} - ESM-only, everything external,
|
|
33
|
+
* `preserveModules` - except it targets `esnext` instead of a Node.js
|
|
34
|
+
* runtime, since the consuming browser (or a downstream bundler) declares
|
|
35
|
+
* its own compilation baseline.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createBrowserLibraryBuildConfig(options: BrowserLibraryBuildConfigOptions): UserConfig;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//#region src/vite/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* The build shape both factories below share: ESM-only, every bare
|
|
4
|
+
* specifier externalized (a dependency stays a real `package.json`
|
|
5
|
+
* dependency instead of getting inlined, so tree-shaking stays viable
|
|
6
|
+
* downstream), and `preserveModules` so `dist` mirrors `src` 1:1 - one
|
|
7
|
+
* output file per source module, which is what lets `package.json#exports`
|
|
8
|
+
* map deep-import paths straight to build output.
|
|
9
|
+
*/
|
|
10
|
+
function createLibraryBuildConfig(target, entry) {
|
|
11
|
+
return { build: {
|
|
12
|
+
target,
|
|
13
|
+
minify: false,
|
|
14
|
+
lib: {
|
|
15
|
+
entry,
|
|
16
|
+
formats: ["es"]
|
|
17
|
+
},
|
|
18
|
+
rollupOptions: {
|
|
19
|
+
external: (id) => !id.startsWith(".") && !id.startsWith("/"),
|
|
20
|
+
output: {
|
|
21
|
+
preserveModules: true,
|
|
22
|
+
preserveModulesRoot: "src",
|
|
23
|
+
entryFileNames: "[name].js"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Vite build config for a Node.js library entry point: ESM output only,
|
|
30
|
+
* targets `node22`, and never bundles a dependency - every bare specifier
|
|
31
|
+
* (`lit`, `node:path`, `@scope/pkg`) is externalized. `preserveModules`
|
|
32
|
+
* keeps one output file per source module instead of one monolithic
|
|
33
|
+
* bundle, so `package.json#exports` can expose deep-import paths.
|
|
34
|
+
*
|
|
35
|
+
* Extend it with `mergeConfig` rather than hand-rolling the shape again:
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { mergeConfig } from 'vite';
|
|
39
|
+
* import { createNodeLibraryBuildConfig } from '@coryrylan/tools/vite';
|
|
40
|
+
*
|
|
41
|
+
* export default mergeConfig(
|
|
42
|
+
* createNodeLibraryBuildConfig({ entry: { index: 'src/index.ts' } }),
|
|
43
|
+
* { plugins: [] },
|
|
44
|
+
* );
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
function createNodeLibraryBuildConfig(options) {
|
|
48
|
+
return createLibraryBuildConfig("node22", options.entry);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Vite build config for a browser library entry point. Same shape as
|
|
52
|
+
* {@link createNodeLibraryBuildConfig} - ESM-only, everything external,
|
|
53
|
+
* `preserveModules` - except it targets `esnext` instead of a Node.js
|
|
54
|
+
* runtime, since the consuming browser (or a downstream bundler) declares
|
|
55
|
+
* its own compilation baseline.
|
|
56
|
+
*/
|
|
57
|
+
function createBrowserLibraryBuildConfig(options) {
|
|
58
|
+
return createLibraryBuildConfig("esnext", options.entry);
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { createBrowserLibraryBuildConfig, createNodeLibraryBuildConfig };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/** Options for {@link dts}. */
|
|
3
|
+
export interface DtsPluginOptions {
|
|
4
|
+
/** Path to the `tsconfig` used for declaration emit, resolved against `process.cwd()`. @default 'tsconfig.build.json' */
|
|
5
|
+
readonly project?: string;
|
|
6
|
+
/** Directory declarations are emitted into, resolved against `process.cwd()`. @default 'dist' */
|
|
7
|
+
readonly outDir?: string;
|
|
8
|
+
/** Package providing the `bin/tsc` entry point to shell out to - swap in `'typescript-go'` for the native compiler alias. @default 'typescript' */
|
|
9
|
+
readonly compilerPackage?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generates the type declaration files that accompany a library's published
|
|
13
|
+
* JS build. Shells out to the consumer's own TypeScript compiler (or the
|
|
14
|
+
* `typescript-go` native-compiler alias, via `compilerPackage`) at build
|
|
15
|
+
* start so declarations are always in sync with the code that just built,
|
|
16
|
+
* then copies over any hand-written `.d.ts` files `tsc` wouldn't otherwise
|
|
17
|
+
* emit - for example, ambient module declarations. The compiler runs
|
|
18
|
+
* asynchronously; watch builds additionally pass `--incremental` with build
|
|
19
|
+
* info stored at `<outDir>/.tsbuildinfo` so a rebuild reuses tsc's prior
|
|
20
|
+
* state instead of retypechecking from scratch.
|
|
21
|
+
*
|
|
22
|
+
* If `compilerPackage` can't be resolved from the consumer's dependency
|
|
23
|
+
* tree, this warns and no-ops rather than throwing - library code never
|
|
24
|
+
* crashes a consumer's build over a misconfigured option.
|
|
25
|
+
*/
|
|
26
|
+
export declare function dts(options?: DtsPluginOptions): Plugin;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
//#region src/vite/plugins/dts.ts
|
|
7
|
+
var DEFAULT_PROJECT = "tsconfig.build.json";
|
|
8
|
+
var DEFAULT_OUT_DIR = "dist";
|
|
9
|
+
var DEFAULT_COMPILER_PACKAGE = "typescript";
|
|
10
|
+
/**
|
|
11
|
+
* Resolves `<compilerPackage>/bin/tsc` from the *consuming* project's own
|
|
12
|
+
* dependency tree, not this package's - a monorepo consumer commonly pins a
|
|
13
|
+
* different TypeScript (or the `typescript-go` native-compiler alias) than
|
|
14
|
+
* whatever this package itself was built with.
|
|
15
|
+
*/
|
|
16
|
+
function resolveTscBinaryPath(compilerPackage) {
|
|
17
|
+
try {
|
|
18
|
+
return resolve(dirname(createRequire(pathToFileURL(join(process.cwd(), "package.json"))).resolve(`${compilerPackage}/package.json`)), "bin/tsc");
|
|
19
|
+
} catch {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Copies every hand-written `src/**\/*.d.ts` (excluding `*.test.d.ts`) into
|
|
25
|
+
* `outDir`, preserving its path relative to `srcDir` - `tsc` only emits
|
|
26
|
+
* declarations for `.ts` sources, so ambient/hand-authored `.d.ts` files
|
|
27
|
+
* never make it into the build output on their own.
|
|
28
|
+
*/
|
|
29
|
+
function copyHandWrittenDeclarationFiles(srcDir, outDir) {
|
|
30
|
+
if (!existsSync(srcDir)) return;
|
|
31
|
+
const declarationFilePaths = readdirSync(srcDir, { recursive: true }).filter((entry) => typeof entry === "string").filter((entry) => entry.endsWith(".d.ts") && !entry.endsWith(".test.d.ts"));
|
|
32
|
+
for (const relativePath of declarationFilePaths) {
|
|
33
|
+
const destination = resolve(outDir, relativePath);
|
|
34
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
35
|
+
copyFileSync(resolve(srcDir, relativePath), destination);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Runs the resolved tsc binary without blocking the event loop. Rejects on a
|
|
40
|
+
* nonzero exit so type errors still fail the build - the one place this
|
|
41
|
+
* package's warn-and-no-op rule doesn't apply, because a silently skipped
|
|
42
|
+
* typecheck would publish broken declarations.
|
|
43
|
+
*/
|
|
44
|
+
function runTsc(args) {
|
|
45
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
46
|
+
const child = spawn(process.execPath, args, { stdio: "inherit" });
|
|
47
|
+
child.on("error", rejectPromise);
|
|
48
|
+
child.on("close", (code) => {
|
|
49
|
+
if (code === 0) resolvePromise();
|
|
50
|
+
else rejectPromise(/* @__PURE__ */ new Error(`tsc exited with code ${String(code)}`));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Generates the type declaration files that accompany a library's published
|
|
56
|
+
* JS build. Shells out to the consumer's own TypeScript compiler (or the
|
|
57
|
+
* `typescript-go` native-compiler alias, via `compilerPackage`) at build
|
|
58
|
+
* start so declarations are always in sync with the code that just built,
|
|
59
|
+
* then copies over any hand-written `.d.ts` files `tsc` wouldn't otherwise
|
|
60
|
+
* emit - for example, ambient module declarations. The compiler runs
|
|
61
|
+
* asynchronously; watch builds additionally pass `--incremental` with build
|
|
62
|
+
* info stored at `<outDir>/.tsbuildinfo` so a rebuild reuses tsc's prior
|
|
63
|
+
* state instead of retypechecking from scratch.
|
|
64
|
+
*
|
|
65
|
+
* If `compilerPackage` can't be resolved from the consumer's dependency
|
|
66
|
+
* tree, this warns and no-ops rather than throwing - library code never
|
|
67
|
+
* crashes a consumer's build over a misconfigured option.
|
|
68
|
+
*/
|
|
69
|
+
function dts(options = {}) {
|
|
70
|
+
const project = options.project ?? DEFAULT_PROJECT;
|
|
71
|
+
const outDir = options.outDir ?? DEFAULT_OUT_DIR;
|
|
72
|
+
const compilerPackage = options.compilerPackage ?? DEFAULT_COMPILER_PACKAGE;
|
|
73
|
+
let isWatchBuild = false;
|
|
74
|
+
return {
|
|
75
|
+
name: "dts",
|
|
76
|
+
apply: "build",
|
|
77
|
+
configResolved(config) {
|
|
78
|
+
isWatchBuild = config.build.watch !== null;
|
|
79
|
+
},
|
|
80
|
+
async buildStart() {
|
|
81
|
+
const cwd = process.cwd();
|
|
82
|
+
const tscPath = resolveTscBinaryPath(compilerPackage);
|
|
83
|
+
if (tscPath === void 0) {
|
|
84
|
+
console.warn(`[@coryrylan/tools/vite] Could not resolve compiler package "${compilerPackage}" from ${cwd} - skipping declaration generation.`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const projectPath = resolve(cwd, project);
|
|
88
|
+
const outDirPath = resolve(cwd, outDir);
|
|
89
|
+
await runTsc([
|
|
90
|
+
tscPath,
|
|
91
|
+
"--project",
|
|
92
|
+
projectPath,
|
|
93
|
+
"--outDir",
|
|
94
|
+
outDirPath,
|
|
95
|
+
...isWatchBuild ? [
|
|
96
|
+
"--incremental",
|
|
97
|
+
"--tsBuildInfoFile",
|
|
98
|
+
join(outDirPath, ".tsbuildinfo")
|
|
99
|
+
] : []
|
|
100
|
+
]);
|
|
101
|
+
copyHandWrittenDeclarationFiles(resolve(cwd, "src"), outDirPath);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { dts };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/** Options for {@link writeIfChanged}. */
|
|
3
|
+
export interface WriteIfChangedPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Output directory to resolve emitted chunk file names against. Only
|
|
6
|
+
* consulted when Rollup's own `output.dir` isn't set on the build.
|
|
7
|
+
*/
|
|
8
|
+
readonly outDir?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Vite plugin that only writes bundle files to disk if their content has
|
|
12
|
+
* changed. Prevents unnecessary file system writes - and the downstream
|
|
13
|
+
* rebuilds/HMR churn a file watcher would otherwise trigger - when a
|
|
14
|
+
* rebuild produces byte-identical output.
|
|
15
|
+
*
|
|
16
|
+
* Uses `writeBundle` instead of `generateBundle` for Rolldown compatibility:
|
|
17
|
+
* Rolldown does not support mutating the bundle object, so the write has to
|
|
18
|
+
* happen after Rollup/Rolldown's own bundle-writing step, not in place of it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function writeIfChanged(options?: WriteIfChangedPluginOptions): Plugin;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { extname, resolve } from "node:path";
|
|
3
|
+
//#region src/vite/plugins/write-if-changed.ts
|
|
4
|
+
var writtenFileContentsByPath = /* @__PURE__ */ new Map();
|
|
5
|
+
/**
|
|
6
|
+
* Vite plugin that only writes bundle files to disk if their content has
|
|
7
|
+
* changed. Prevents unnecessary file system writes - and the downstream
|
|
8
|
+
* rebuilds/HMR churn a file watcher would otherwise trigger - when a
|
|
9
|
+
* rebuild produces byte-identical output.
|
|
10
|
+
*
|
|
11
|
+
* Uses `writeBundle` instead of `generateBundle` for Rolldown compatibility:
|
|
12
|
+
* Rolldown does not support mutating the bundle object, so the write has to
|
|
13
|
+
* happen after Rollup/Rolldown's own bundle-writing step, not in place of it.
|
|
14
|
+
*/
|
|
15
|
+
function writeIfChanged(options = {}) {
|
|
16
|
+
return {
|
|
17
|
+
name: "write-if-changed",
|
|
18
|
+
writeBundle(outputOptions, bundle) {
|
|
19
|
+
const dir = outputOptions.dir ?? options.outDir;
|
|
20
|
+
if (dir === void 0) return;
|
|
21
|
+
for (const bundleFile of Object.values(bundle)) {
|
|
22
|
+
if (bundleFile.type !== "chunk" || extname(bundleFile.fileName) !== ".js") continue;
|
|
23
|
+
const filePath = resolve(dir, bundleFile.fileName);
|
|
24
|
+
if (writtenFileContentsByPath.get(filePath) === bundleFile.code) continue;
|
|
25
|
+
writtenFileContentsByPath.set(filePath, bundleFile.code);
|
|
26
|
+
mkdirSync(resolve(filePath, ".."), { recursive: true });
|
|
27
|
+
writeFileSync(filePath, bundleFile.code);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { writeIfChanged };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ViteUserConfig } from 'vitest/config';
|
|
2
|
+
/**
|
|
3
|
+
* Preset Vitest config for browser-mode component tests against a real
|
|
4
|
+
* Chromium instance via the Playwright provider. Separate entry point from
|
|
5
|
+
* `./index.js` so a consumer without `@vitest/browser-playwright` installed
|
|
6
|
+
* never loads this module's import of it.
|
|
7
|
+
*
|
|
8
|
+
* Extend it with `mergeConfig`:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* // vitest.config.ts
|
|
12
|
+
* import { mergeConfig, defineConfig } from 'vitest/config';
|
|
13
|
+
* import { browserTestConfig } from '@coryrylan/tools/vitest/browser';
|
|
14
|
+
*
|
|
15
|
+
* export default mergeConfig(
|
|
16
|
+
* browserTestConfig,
|
|
17
|
+
* defineConfig({
|
|
18
|
+
* test: { setupFiles: ['./test-setup.ts'] },
|
|
19
|
+
* }),
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Requires the optional peers `@vitest/browser-playwright`,
|
|
24
|
+
* `@vitest/coverage-istanbul`, and `playwright`, plus a browser installed
|
|
25
|
+
* via `pnpm exec playwright install chromium`.
|
|
26
|
+
*
|
|
27
|
+
* `CI`/`--watch` detection is resolved once at import time (see `isCi` and
|
|
28
|
+
* `isWatchMode` above) - changing either after this module loads has no
|
|
29
|
+
* effect on an already-built config.
|
|
30
|
+
*/
|
|
31
|
+
export declare const browserTestConfig: ViteUserConfig;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import { playwright } from "@vitest/browser-playwright";
|
|
3
|
+
//#region src/vitest/browser.ts
|
|
4
|
+
var isCi = process.env["CI"] !== void 0;
|
|
5
|
+
var isWatchMode = process.argv.includes("--watch");
|
|
6
|
+
/**
|
|
7
|
+
* Preset Vitest config for browser-mode component tests against a real
|
|
8
|
+
* Chromium instance via the Playwright provider. Separate entry point from
|
|
9
|
+
* `./index.js` so a consumer without `@vitest/browser-playwright` installed
|
|
10
|
+
* never loads this module's import of it.
|
|
11
|
+
*
|
|
12
|
+
* Extend it with `mergeConfig`:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* // vitest.config.ts
|
|
16
|
+
* import { mergeConfig, defineConfig } from 'vitest/config';
|
|
17
|
+
* import { browserTestConfig } from '@coryrylan/tools/vitest/browser';
|
|
18
|
+
*
|
|
19
|
+
* export default mergeConfig(
|
|
20
|
+
* browserTestConfig,
|
|
21
|
+
* defineConfig({
|
|
22
|
+
* test: { setupFiles: ['./test-setup.ts'] },
|
|
23
|
+
* }),
|
|
24
|
+
* );
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Requires the optional peers `@vitest/browser-playwright`,
|
|
28
|
+
* `@vitest/coverage-istanbul`, and `playwright`, plus a browser installed
|
|
29
|
+
* via `pnpm exec playwright install chromium`.
|
|
30
|
+
*
|
|
31
|
+
* `CI`/`--watch` detection is resolved once at import time (see `isCi` and
|
|
32
|
+
* `isWatchMode` above) - changing either after this module loads has no
|
|
33
|
+
* effect on an already-built config.
|
|
34
|
+
*/
|
|
35
|
+
var browserTestConfig = { test: {
|
|
36
|
+
globals: true,
|
|
37
|
+
include: ["src/**/*.test.ts"],
|
|
38
|
+
retry: 2,
|
|
39
|
+
...isCi ? {
|
|
40
|
+
maxWorkers: 1,
|
|
41
|
+
maxConcurrency: 1
|
|
42
|
+
} : {},
|
|
43
|
+
testTimeout: 6e4,
|
|
44
|
+
hookTimeout: 3e4,
|
|
45
|
+
browser: {
|
|
46
|
+
fileParallelism: !isCi,
|
|
47
|
+
enabled: true,
|
|
48
|
+
provider: playwright({ launchOptions: {
|
|
49
|
+
args: [
|
|
50
|
+
"--disable-dev-shm-usage",
|
|
51
|
+
"--disable-setuid-sandbox",
|
|
52
|
+
"--disable-software-rasterizer",
|
|
53
|
+
"--no-sandbox",
|
|
54
|
+
"--disable-gpu",
|
|
55
|
+
"--disable-extensions",
|
|
56
|
+
"--disable-background-networking",
|
|
57
|
+
"--disable-default-apps",
|
|
58
|
+
"--disable-sync",
|
|
59
|
+
"--disable-translate",
|
|
60
|
+
"--metrics-recording-only",
|
|
61
|
+
"--mute-audio",
|
|
62
|
+
"--no-first-run",
|
|
63
|
+
"--safebrowsing-disable-auto-update",
|
|
64
|
+
"--disable-features=TranslateUI",
|
|
65
|
+
"--disable-features=BlinkGenPropertyTrees",
|
|
66
|
+
"--disable-ipc-flooding-protection"
|
|
67
|
+
],
|
|
68
|
+
timeout: 12e4
|
|
69
|
+
} }),
|
|
70
|
+
headless: !isWatchMode,
|
|
71
|
+
instances: [{ browser: "chromium" }]
|
|
72
|
+
},
|
|
73
|
+
coverage: {
|
|
74
|
+
provider: "istanbul",
|
|
75
|
+
reportsDirectory: "./coverage/unit",
|
|
76
|
+
reporter: [
|
|
77
|
+
["lcov", { file: "coverage.dat" }],
|
|
78
|
+
"html",
|
|
79
|
+
"json-summary"
|
|
80
|
+
],
|
|
81
|
+
thresholds: {
|
|
82
|
+
lines: 90,
|
|
83
|
+
branches: 90,
|
|
84
|
+
functions: 90,
|
|
85
|
+
statements: 90
|
|
86
|
+
},
|
|
87
|
+
watermarks: {
|
|
88
|
+
statements: [80, 90],
|
|
89
|
+
functions: [80, 90],
|
|
90
|
+
branches: [80, 90],
|
|
91
|
+
lines: [80, 90]
|
|
92
|
+
},
|
|
93
|
+
include: ["src/**/*.ts"],
|
|
94
|
+
exclude: [
|
|
95
|
+
"**/dist/**",
|
|
96
|
+
"**/*.test.ts",
|
|
97
|
+
"**/*.d.ts",
|
|
98
|
+
"**/*.examples.ts",
|
|
99
|
+
"vite.*.ts",
|
|
100
|
+
"vitest.*.ts"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
} };
|
|
104
|
+
//#endregion
|
|
105
|
+
export { browserTestConfig };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ViteUserConfig } from 'vitest/config';
|
|
2
|
+
/**
|
|
3
|
+
* Preset Vitest config for node-environment unit tests of libraries and
|
|
4
|
+
* tooling: Node globals, no DOM, `test.globals` enabled so specs can call
|
|
5
|
+
* `describe`/`it`/`expect` without importing them.
|
|
6
|
+
*
|
|
7
|
+
* Extend it with `mergeConfig` rather than spreading manually, so array
|
|
8
|
+
* fields (e.g. `test.include`) merge instead of getting clobbered:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* // vitest.config.ts
|
|
12
|
+
* import { mergeConfig, defineConfig } from 'vitest/config';
|
|
13
|
+
* import { nodeTestConfig } from '@coryrylan/tools/vitest';
|
|
14
|
+
*
|
|
15
|
+
* export default mergeConfig(
|
|
16
|
+
* nodeTestConfig,
|
|
17
|
+
* defineConfig({
|
|
18
|
+
* test: { coverage: { provider: 'istanbul' } },
|
|
19
|
+
* }),
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* This module has zero runtime dependency on `@vitest/browser-playwright` -
|
|
24
|
+
* it is safe to import without that optional peer installed. Browser-mode
|
|
25
|
+
* config lives in the sibling `./browser.js` entry point instead, so a
|
|
26
|
+
* plain node consumer never pulls in a Playwright import transitively.
|
|
27
|
+
*/
|
|
28
|
+
export declare const nodeTestConfig: ViteUserConfig;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/vitest/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Preset Vitest config for node-environment unit tests of libraries and
|
|
4
|
+
* tooling: Node globals, no DOM, `test.globals` enabled so specs can call
|
|
5
|
+
* `describe`/`it`/`expect` without importing them.
|
|
6
|
+
*
|
|
7
|
+
* Extend it with `mergeConfig` rather than spreading manually, so array
|
|
8
|
+
* fields (e.g. `test.include`) merge instead of getting clobbered:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* // vitest.config.ts
|
|
12
|
+
* import { mergeConfig, defineConfig } from 'vitest/config';
|
|
13
|
+
* import { nodeTestConfig } from '@coryrylan/tools/vitest';
|
|
14
|
+
*
|
|
15
|
+
* export default mergeConfig(
|
|
16
|
+
* nodeTestConfig,
|
|
17
|
+
* defineConfig({
|
|
18
|
+
* test: { coverage: { provider: 'istanbul' } },
|
|
19
|
+
* }),
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* This module has zero runtime dependency on `@vitest/browser-playwright` -
|
|
24
|
+
* it is safe to import without that optional peer installed. Browser-mode
|
|
25
|
+
* config lives in the sibling `./browser.js` entry point instead, so a
|
|
26
|
+
* plain node consumer never pulls in a Playwright import transitively.
|
|
27
|
+
*/
|
|
28
|
+
var nodeTestConfig = { test: {
|
|
29
|
+
globals: true,
|
|
30
|
+
environment: "node",
|
|
31
|
+
include: ["src/**/*.test.ts"]
|
|
32
|
+
} };
|
|
33
|
+
//#endregion
|
|
34
|
+
export { nodeTestConfig };
|