@typed/vite-plugin 0.17.1 → 1.0.0-beta.1
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 +96 -0
- package/dist/index.d.ts +77 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -5
- package/package.json +27 -29
- package/src/index.test.ts +97 -0
- package/src/index.ts +183 -4
- package/dist/cjs/constants.d.ts +0 -2
- package/dist/cjs/constants.d.ts.map +0 -1
- package/dist/cjs/constants.js +0 -5
- package/dist/cjs/constants.js.map +0 -1
- package/dist/cjs/index.d.ts +0 -5
- package/dist/cjs/index.d.ts.map +0 -1
- package/dist/cjs/index.js +0 -26
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/resolveTypedConfig.d.ts +0 -20
- package/dist/cjs/resolveTypedConfig.d.ts.map +0 -1
- package/dist/cjs/resolveTypedConfig.js +0 -16
- package/dist/cjs/resolveTypedConfig.js.map +0 -1
- package/dist/cjs/vite-plugin.d.ts +0 -56
- package/dist/cjs/vite-plugin.d.ts.map +0 -1
- package/dist/cjs/vite-plugin.js +0 -212
- package/dist/cjs/vite-plugin.js.map +0 -1
- package/dist/constants.d.ts +0 -2
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -2
- package/dist/constants.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/resolveTypedConfig.d.ts +0 -20
- package/dist/resolveTypedConfig.d.ts.map +0 -1
- package/dist/resolveTypedConfig.js +0 -12
- package/dist/resolveTypedConfig.js.map +0 -1
- package/dist/tsconfig.cjs.build.tsbuildinfo +0 -1
- package/dist/vite-plugin.d.ts +0 -56
- package/dist/vite-plugin.d.ts.map +0 -1
- package/dist/vite-plugin.js +0 -179
- package/dist/vite-plugin.js.map +0 -1
- package/eslintrc.json +0 -3
- package/project.json +0 -44
- package/readme.md +0 -3
- package/src/constants.ts +0 -1
- package/src/resolveTypedConfig.ts +0 -35
- package/src/vite-plugin.ts +0 -312
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.cjs.build.json +0 -13
- package/tsconfig.json +0 -18
- package/vite.config.js +0 -3
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @typed/vite-plugin
|
|
2
|
+
|
|
3
|
+
> **Beta:** This package is in beta; APIs may change.
|
|
4
|
+
|
|
5
|
+
`@typed/vite-plugin` provides a **one-stop Vite preset**: tsconfig path resolution, virtual modules (router + HttpApi from @typed/app), optional bundle analyzer, and Brotli compression. Use it as the main plugin array for typed-smol Vite projects.
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
`typedVitePlugin` is the recommended way to configure Vite for typed-smol apps. One call gives you: path resolution from `tsconfig.json`, virtual `router:` and `api:` imports that generate typed route matchers and API clients from convention-based source, optional bundle analysis, and Brotli compression for build output.
|
|
10
|
+
|
|
11
|
+
## Capabilities
|
|
12
|
+
|
|
13
|
+
| Area | What you get |
|
|
14
|
+
|------|--------------|
|
|
15
|
+
| **Router VM** | `router:./path` → typed Matcher from route files |
|
|
16
|
+
| **HttpApi VM** | `api:./path` → typed Api + Client + OpenAPI (when `apiVmOptions` set) |
|
|
17
|
+
| **TypeInfo** | Structural type-checking of route/endpoint contracts (when `createTypeInfoApiSession` provided) |
|
|
18
|
+
| **tsconfig paths** | Path alias resolution (default: on) |
|
|
19
|
+
| **Analyzer** | `dist/stats.html` treemap when `ANALYZE=1` |
|
|
20
|
+
| **Compression** | Brotli `.br` for build output (default: on) |
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
```mermaid
|
|
25
|
+
flowchart TB
|
|
26
|
+
subgraph plugins [Plugin array from typedVitePlugin]
|
|
27
|
+
Tsconfig[tsconfig paths]
|
|
28
|
+
VM[virtualModulesVitePlugin]
|
|
29
|
+
Viz[visualizer]
|
|
30
|
+
Comp[viteCompression]
|
|
31
|
+
end
|
|
32
|
+
subgraph vmResolver [VM resolver]
|
|
33
|
+
RouterVM[router VM]
|
|
34
|
+
ApiVM[HttpApi VM]
|
|
35
|
+
end
|
|
36
|
+
VM --> vmResolver
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Dependencies
|
|
40
|
+
|
|
41
|
+
- `@typed/app`
|
|
42
|
+
- `@typed/virtual-modules`
|
|
43
|
+
- `@typed/virtual-modules-vite`
|
|
44
|
+
|
|
45
|
+
Peer: `vite` (>=5).
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm add @typed/vite-plugin @typed/app
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { defineConfig } from "vite";
|
|
57
|
+
import { typedVitePlugin } from "@typed/vite-plugin";
|
|
58
|
+
|
|
59
|
+
export default defineConfig({
|
|
60
|
+
plugins: typedVitePlugin(),
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For router VM type-checking, pass `createTypeInfoApiSession` (e.g. from a custom Vite plugin that builds a TypeScript program):
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { typedVitePlugin } from "@typed/vite-plugin";
|
|
68
|
+
import { createTypeInfoApiSessionForApp } from "@typed/app";
|
|
69
|
+
|
|
70
|
+
export default defineConfig({
|
|
71
|
+
plugins: typedVitePlugin({
|
|
72
|
+
createTypeInfoApiSession: ({ ts, program }) =>
|
|
73
|
+
createTypeInfoApiSessionForApp({ ts, program }),
|
|
74
|
+
routerVmOptions: {},
|
|
75
|
+
apiVmOptions: {},
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API overview
|
|
81
|
+
|
|
82
|
+
- **`typedVitePlugin(options)`** — Returns an array of Vite plugins.
|
|
83
|
+
- **`createTypedViteResolver(options)`** — Builds the virtual-module resolver (for tests or custom compositions).
|
|
84
|
+
|
|
85
|
+
## Options
|
|
86
|
+
|
|
87
|
+
| Option | Type | Default | Description |
|
|
88
|
+
| ------ | ---- | ------- | ----------- |
|
|
89
|
+
| `routerVmOptions` | `RouterVirtualModulePluginOptions` | `{}` | Options for the router VM plugin. |
|
|
90
|
+
| `apiVmOptions` | `HttpApiVirtualModulePluginOptions` | — | When set, enables the HttpApi VM plugin. |
|
|
91
|
+
| `createTypeInfoApiSession` | `CreateTypeInfoApiSession` | — | Required for router VM type-checking in dev. |
|
|
92
|
+
| `tsconfigPaths` | `boolean \| object` | `true` | Enable tsconfig path resolution. |
|
|
93
|
+
| `analyze` | `boolean \| object` | `process.env.ANALYZE === '1'` | Enable bundle analyzer (dist/stats.html). |
|
|
94
|
+
| `warnOnError` | `boolean` | `true` | Log virtual module resolution errors. |
|
|
95
|
+
| `compression` | `boolean \| object` | `true` | Brotli compression for build output. |
|
|
96
|
+
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @typed/vite-plugin — One-stop Vite preset: tsconfig paths, bundle analyzer,
|
|
3
|
+
* Brotli compression, virtual-modules Vite plugin, and @typed/app VM plugins.
|
|
4
|
+
*/
|
|
5
|
+
import type { Plugin } from "vite";
|
|
6
|
+
import type { CreateTypeInfoApiSession, VirtualModuleResolver } from "@typed/virtual-modules";
|
|
7
|
+
/**
|
|
8
|
+
* Options passed through to the HttpApi virtual-module plugin when enabled.
|
|
9
|
+
* Forwarded to createHttpApiVirtualModulePlugin from the package that provides it
|
|
10
|
+
* (e.g. @typed/app when the export is added). See httpapi-virtual-module-plugin spec.
|
|
11
|
+
*/
|
|
12
|
+
export interface HttpApiVirtualModulePluginOptions {
|
|
13
|
+
readonly [key: string]: unknown;
|
|
14
|
+
}
|
|
15
|
+
/** Options for vite-plugin-compression when compression is enabled. */
|
|
16
|
+
export type TypedViteCompressionOptions = boolean | {
|
|
17
|
+
readonly algorithm?: "gzip" | "brotliCompress" | "deflate" | "deflateRaw";
|
|
18
|
+
readonly ext?: string;
|
|
19
|
+
readonly threshold?: number;
|
|
20
|
+
readonly [key: string]: unknown;
|
|
21
|
+
};
|
|
22
|
+
export interface TypedVitePluginOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Options for the router VM plugin from @typed/app.
|
|
25
|
+
*/
|
|
26
|
+
readonly routerVmOptions?: import("@typed/app").RouterVirtualModulePluginOptions;
|
|
27
|
+
/**
|
|
28
|
+
* Options for the HttpApi VM plugin from @typed/app. HttpApi VM plugin is always
|
|
29
|
+
* registered (router first, then HttpApi). Use this to customize its behavior.
|
|
30
|
+
*/
|
|
31
|
+
readonly apiVmOptions?: HttpApiVirtualModulePluginOptions;
|
|
32
|
+
/**
|
|
33
|
+
* Session factory for TypeInfo API. When not provided, a Language Service-backed
|
|
34
|
+
* session is auto-created from the project's tsconfig (evolves as files change).
|
|
35
|
+
* Override for custom session setup.
|
|
36
|
+
*/
|
|
37
|
+
readonly createTypeInfoApiSession?: CreateTypeInfoApiSession;
|
|
38
|
+
/**
|
|
39
|
+
* Enable tsconfig path resolution. Default true.
|
|
40
|
+
*/
|
|
41
|
+
readonly tsconfigPaths?: boolean | Record<string, unknown>;
|
|
42
|
+
/**
|
|
43
|
+
* Enable bundle analyzer. Default: process.env.ANALYZE === '1'.
|
|
44
|
+
*/
|
|
45
|
+
readonly analyze?: boolean | {
|
|
46
|
+
filename?: string;
|
|
47
|
+
open?: boolean;
|
|
48
|
+
template?: string;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* When true, virtual module resolution errors are logged. Default true.
|
|
52
|
+
*/
|
|
53
|
+
readonly warnOnError?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Enable Brotli compression for build. Default true.
|
|
56
|
+
* Set false to disable, or pass options to customize (algorithm, ext, threshold).
|
|
57
|
+
*/
|
|
58
|
+
readonly compression?: TypedViteCompressionOptions;
|
|
59
|
+
}
|
|
60
|
+
/** Optional dependency injection for createTypedViteResolver (e.g. for tests). */
|
|
61
|
+
export interface TypedViteResolverDependencies {
|
|
62
|
+
createHttpApiVirtualModulePlugin?: (opts: HttpApiVirtualModulePluginOptions) => import("@typed/virtual-modules").VirtualModulePlugin;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Invariant: ALL @typed/app VM plugins are always registered. There are no optional
|
|
66
|
+
* or conditional app plugins. When adding a new VM plugin to @typed/app, add it here.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createTypedViteResolver(options?: TypedVitePluginOptions, dependencies?: TypedViteResolverDependencies): VirtualModuleResolver;
|
|
69
|
+
/**
|
|
70
|
+
* Returns Vite plugins: tsconfig paths, virtual modules (@typed/app), and optional bundle analyzer.
|
|
71
|
+
* Use as: `defineConfig({ plugins: typedVitePlugin() })`.
|
|
72
|
+
*
|
|
73
|
+
* When createTypeInfoApiSession is not provided, the plugin automatically creates a
|
|
74
|
+
* Language Service-backed session from the project's tsconfig. The type program evolves
|
|
75
|
+
* as files change during dev. Pass createTypeInfoApiSession to override.
|
|
76
|
+
*/
|
|
77
|
+
export declare function typedVitePlugin(options?: TypedVitePluginOptions): Plugin[];
|
|
5
78
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAgB9F;;;;GAIG;AACH,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,uEAAuE;AACvE,MAAM,MAAM,2BAA2B,GACnC,OAAO,GACP;IACE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,GAAG,YAAY,CAAC;IAC1E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEN,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,YAAY,EAAE,gCAAgC,CAAC;IAEjF;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,iCAAiC,CAAC;IAE1D;;;;OAIG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IAE7D;;OAEG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3D;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAEtF;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,2BAA2B,CAAC;CACpD;AAED,kFAAkF;AAClF,MAAM,WAAW,6BAA6B;IAC5C,gCAAgC,CAAC,EAAE,CACjC,IAAI,EAAE,iCAAiC,KACpC,OAAO,wBAAwB,EAAE,mBAAmB,CAAC;CAC3D;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,sBAA2B,EACpC,YAAY,CAAC,EAAE,6BAA6B,GAC3C,qBAAqB,CASvB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,EAAE,CAuE9E"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { collectTypeTargetSpecsFromPlugins, createLanguageServiceSessionFactory, PluginManager, } from "@typed/virtual-modules";
|
|
2
|
+
import { createHttpApiVirtualModulePlugin, createRouterVirtualModulePlugin, } from "@typed/app";
|
|
3
|
+
import { virtualModulesVitePlugin } from "@typed/virtual-modules-vite";
|
|
4
|
+
import ts from "typescript";
|
|
5
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
6
|
+
import { visualizer } from "rollup-plugin-visualizer";
|
|
7
|
+
import viteCompression from "vite-plugin-compression";
|
|
8
|
+
/**
|
|
9
|
+
* Invariant: ALL @typed/app VM plugins are always registered. There are no optional
|
|
10
|
+
* or conditional app plugins. When adding a new VM plugin to @typed/app, add it here.
|
|
11
|
+
*/
|
|
12
|
+
export function createTypedViteResolver(options = {}, dependencies) {
|
|
13
|
+
const httpApiFactory = dependencies?.createHttpApiVirtualModulePlugin ??
|
|
14
|
+
createHttpApiVirtualModulePlugin;
|
|
15
|
+
const plugins = [
|
|
16
|
+
createRouterVirtualModulePlugin(options.routerVmOptions ?? {}),
|
|
17
|
+
httpApiFactory(options.apiVmOptions ?? {}),
|
|
18
|
+
];
|
|
19
|
+
return new PluginManager(plugins);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Returns Vite plugins: tsconfig paths, virtual modules (@typed/app), and optional bundle analyzer.
|
|
23
|
+
* Use as: `defineConfig({ plugins: typedVitePlugin() })`.
|
|
24
|
+
*
|
|
25
|
+
* When createTypeInfoApiSession is not provided, the plugin automatically creates a
|
|
26
|
+
* Language Service-backed session from the project's tsconfig. The type program evolves
|
|
27
|
+
* as files change during dev. Pass createTypeInfoApiSession to override.
|
|
28
|
+
*/
|
|
29
|
+
export function typedVitePlugin(options = {}) {
|
|
30
|
+
const resolver = createTypedViteResolver(options);
|
|
31
|
+
const analyze = options.analyze ?? (process.env.ANALYZE === "1" ? true : false);
|
|
32
|
+
let createTypeInfoApiSession = options.createTypeInfoApiSession;
|
|
33
|
+
if (createTypeInfoApiSession === undefined) {
|
|
34
|
+
try {
|
|
35
|
+
const manager = resolver;
|
|
36
|
+
const typeTargetSpecs = collectTypeTargetSpecsFromPlugins(manager.plugins);
|
|
37
|
+
createTypeInfoApiSession = createLanguageServiceSessionFactory({
|
|
38
|
+
ts,
|
|
39
|
+
projectRoot: process.cwd(),
|
|
40
|
+
typeTargetSpecs,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Graceful degradation: no session, plugins get noop TypeInfoApi
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const plugins = [];
|
|
48
|
+
if (options.tsconfigPaths !== false) {
|
|
49
|
+
const pathsOpts = typeof options.tsconfigPaths === "object" ? options.tsconfigPaths : {};
|
|
50
|
+
plugins.push(tsconfigPaths(pathsOpts));
|
|
51
|
+
}
|
|
52
|
+
plugins.push(virtualModulesVitePlugin({
|
|
53
|
+
resolver,
|
|
54
|
+
createTypeInfoApiSession,
|
|
55
|
+
warnOnError: options.warnOnError ?? true,
|
|
56
|
+
}));
|
|
57
|
+
if (analyze) {
|
|
58
|
+
const vizOpts = typeof analyze === "object"
|
|
59
|
+
? analyze
|
|
60
|
+
: { filename: "dist/stats.html", template: "treemap" };
|
|
61
|
+
plugins.push(visualizer({
|
|
62
|
+
filename: vizOpts.filename ?? "dist/stats.html",
|
|
63
|
+
open: vizOpts.open ?? false,
|
|
64
|
+
template: vizOpts.template ?? "treemap",
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
const compression = options.compression ?? true;
|
|
68
|
+
if (compression !== false) {
|
|
69
|
+
const compressionOpts = typeof compression === "object"
|
|
70
|
+
? {
|
|
71
|
+
algorithm: "brotliCompress",
|
|
72
|
+
ext: ".br",
|
|
73
|
+
threshold: 1024,
|
|
74
|
+
...compression,
|
|
75
|
+
}
|
|
76
|
+
: {
|
|
77
|
+
algorithm: "brotliCompress",
|
|
78
|
+
ext: ".br",
|
|
79
|
+
threshold: 1024,
|
|
80
|
+
};
|
|
81
|
+
plugins.push(viteCompression(compressionOpts));
|
|
82
|
+
}
|
|
83
|
+
return plugins;
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -1,42 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typed/vite-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"module": "dist/index.js",
|
|
7
|
-
"typings": "dist/index.d.ts",
|
|
8
|
-
"types": "dist/index.d.ts",
|
|
9
5
|
"exports": {
|
|
10
6
|
".": {
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"types": "./dist/index.d.ts"
|
|
14
|
-
},
|
|
15
|
-
"./*": {
|
|
16
|
-
"import": "./dist/*.js",
|
|
17
|
-
"require": "./dist/cjs/*.js",
|
|
18
|
-
"types": "./dist/*.d.ts"
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
19
9
|
}
|
|
20
10
|
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "[ -d dist ] || rm -f tsconfig.tsbuildinfo; tsc",
|
|
16
|
+
"test": "vitest run --passWithNoTests"
|
|
17
|
+
},
|
|
21
18
|
"dependencies": {
|
|
22
|
-
"@
|
|
23
|
-
"@typed/virtual-
|
|
24
|
-
"
|
|
25
|
-
"rollup-plugin-visualizer": "^5.
|
|
26
|
-
"vavite": "^2.0.2",
|
|
27
|
-
"vite": "^4.4.9",
|
|
19
|
+
"@typed/app": "workspace:*",
|
|
20
|
+
"@typed/virtual-modules": "workspace:*",
|
|
21
|
+
"@typed/virtual-modules-vite": "workspace:*",
|
|
22
|
+
"rollup-plugin-visualizer": "^5.12.0",
|
|
28
23
|
"vite-plugin-compression": "^0.5.1",
|
|
29
|
-
"vite-tsconfig-paths": "^
|
|
24
|
+
"vite-tsconfig-paths": "^6.1.0"
|
|
30
25
|
},
|
|
31
|
-
"
|
|
32
|
-
"
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^25.3.0",
|
|
28
|
+
"typescript": "catalog:",
|
|
29
|
+
"vite": "^7.3.1",
|
|
30
|
+
"vitest": "catalog:"
|
|
33
31
|
},
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
"
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"typescript": ">=5.0.0",
|
|
34
|
+
"vite": ">=5.0.0"
|
|
37
35
|
},
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src"
|
|
39
|
+
]
|
|
42
40
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for typedVitePlugin and createTypedViteResolver: plugin order and options pass-through.
|
|
3
|
+
* See .docs/specs/httpapi-virtual-module-plugin/spec.md (Vite Plugin Integration Surface)
|
|
4
|
+
* and testing-strategy.md (typedVitePlugin registration order and option wiring).
|
|
5
|
+
*/
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { PluginManager } from "@typed/virtual-modules";
|
|
8
|
+
import type { VirtualModulePlugin } from "@typed/virtual-modules";
|
|
9
|
+
import {
|
|
10
|
+
createTypedViteResolver,
|
|
11
|
+
typedVitePlugin,
|
|
12
|
+
type HttpApiVirtualModulePluginOptions,
|
|
13
|
+
} from "./index.js";
|
|
14
|
+
|
|
15
|
+
function fakeHttpApiPlugin(opts: HttpApiVirtualModulePluginOptions): VirtualModulePlugin {
|
|
16
|
+
return {
|
|
17
|
+
name: "httpapi-virtual-module",
|
|
18
|
+
shouldResolve: () => false,
|
|
19
|
+
build: () => "",
|
|
20
|
+
_testOpts: opts,
|
|
21
|
+
} as VirtualModulePlugin & { _testOpts: HttpApiVirtualModulePluginOptions };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("createTypedViteResolver", () => {
|
|
25
|
+
it("always registers router and HttpApi VM plugins", () => {
|
|
26
|
+
const resolver = createTypedViteResolver({});
|
|
27
|
+
expect(resolver).toBeInstanceOf(PluginManager);
|
|
28
|
+
const manager = resolver as PluginManager;
|
|
29
|
+
expect(manager.plugins).toHaveLength(2);
|
|
30
|
+
expect(manager.plugins[0].name).toBe("router-virtual-module");
|
|
31
|
+
expect(manager.plugins[1].name).toBe("httpapi-virtual-module");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("uses DI override for HttpApi plugin when provided", () => {
|
|
35
|
+
const resolver = createTypedViteResolver(
|
|
36
|
+
{ apiVmOptions: { prefix: "api:" } },
|
|
37
|
+
{ createHttpApiVirtualModulePlugin: fakeHttpApiPlugin },
|
|
38
|
+
);
|
|
39
|
+
const manager = resolver as PluginManager;
|
|
40
|
+
expect(manager.plugins).toHaveLength(2);
|
|
41
|
+
expect(manager.plugins[0].name).toBe("router-virtual-module");
|
|
42
|
+
expect(manager.plugins[1].name).toBe("httpapi-virtual-module");
|
|
43
|
+
const apiPlugin = manager.plugins[1] as VirtualModulePlugin & {
|
|
44
|
+
_testOpts: HttpApiVirtualModulePluginOptions;
|
|
45
|
+
};
|
|
46
|
+
expect(apiPlugin._testOpts).toEqual({ prefix: "api:" });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("passes apiVmOptions through to createHttpApiVirtualModulePlugin", () => {
|
|
50
|
+
const opts: HttpApiVirtualModulePluginOptions = { custom: "value", count: 1 };
|
|
51
|
+
const resolver = createTypedViteResolver(
|
|
52
|
+
{ apiVmOptions: opts },
|
|
53
|
+
{ createHttpApiVirtualModulePlugin: fakeHttpApiPlugin },
|
|
54
|
+
);
|
|
55
|
+
const manager = resolver as PluginManager;
|
|
56
|
+
const apiPlugin = manager.plugins[1] as VirtualModulePlugin & {
|
|
57
|
+
_testOpts: HttpApiVirtualModulePluginOptions;
|
|
58
|
+
};
|
|
59
|
+
expect(apiPlugin._testOpts).toEqual(opts);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("uses routerVmOptions for the router plugin", () => {
|
|
63
|
+
const resolver = createTypedViteResolver({
|
|
64
|
+
routerVmOptions: { prefix: "routes:", name: "custom-router" },
|
|
65
|
+
});
|
|
66
|
+
const manager = resolver as PluginManager;
|
|
67
|
+
expect(manager.plugins).toHaveLength(2);
|
|
68
|
+
expect(manager.plugins[0].name).toBe("custom-router");
|
|
69
|
+
expect(manager.plugins[1].name).toBe("httpapi-virtual-module");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("typedVitePlugin", () => {
|
|
74
|
+
it("returns a non-empty plugin array", () => {
|
|
75
|
+
const plugins = typedVitePlugin();
|
|
76
|
+
expect(Array.isArray(plugins)).toBe(true);
|
|
77
|
+
expect(plugins.length).toBeGreaterThan(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("returns virtual-modules plugin with resolveId and load", () => {
|
|
81
|
+
const plugins = typedVitePlugin({ tsconfigPaths: false, compression: false });
|
|
82
|
+
const virtualPlugin = plugins.find(
|
|
83
|
+
(p) => p && typeof p === "object" && "name" in p && (p as { name?: string }).name === "virtual-modules",
|
|
84
|
+
);
|
|
85
|
+
expect(virtualPlugin).toBeDefined();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("auto-creates LS-backed session when createTypeInfoApiSession is not provided", () => {
|
|
89
|
+
const plugins = typedVitePlugin({ tsconfigPaths: false, compression: false });
|
|
90
|
+
const virtualPlugin = plugins.find(
|
|
91
|
+
(p) => p && typeof p === "object" && "name" in p && (p as { name?: string }).name === "virtual-modules",
|
|
92
|
+
);
|
|
93
|
+
expect(virtualPlugin).toBeDefined();
|
|
94
|
+
expect(virtualPlugin).toHaveProperty("resolveId");
|
|
95
|
+
expect(virtualPlugin).toHaveProperty("load");
|
|
96
|
+
});
|
|
97
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,186 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @typed/vite-plugin — One-stop Vite preset: tsconfig paths, bundle analyzer,
|
|
3
|
+
* Brotli compression, virtual-modules Vite plugin, and @typed/app VM plugins.
|
|
4
|
+
*/
|
|
5
|
+
import type { Plugin } from "vite";
|
|
6
|
+
import type { CreateTypeInfoApiSession, VirtualModuleResolver } from "@typed/virtual-modules";
|
|
7
|
+
import {
|
|
8
|
+
collectTypeTargetSpecsFromPlugins,
|
|
9
|
+
createLanguageServiceSessionFactory,
|
|
10
|
+
PluginManager,
|
|
11
|
+
} from "@typed/virtual-modules";
|
|
12
|
+
import {
|
|
13
|
+
createHttpApiVirtualModulePlugin,
|
|
14
|
+
createRouterVirtualModulePlugin,
|
|
15
|
+
} from "@typed/app";
|
|
16
|
+
import { virtualModulesVitePlugin } from "@typed/virtual-modules-vite";
|
|
17
|
+
import ts from "typescript";
|
|
18
|
+
import tsconfigPaths from "vite-tsconfig-paths";
|
|
19
|
+
import { visualizer } from "rollup-plugin-visualizer";
|
|
20
|
+
import viteCompression from "vite-plugin-compression";
|
|
2
21
|
|
|
3
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Options passed through to the HttpApi virtual-module plugin when enabled.
|
|
24
|
+
* Forwarded to createHttpApiVirtualModulePlugin from the package that provides it
|
|
25
|
+
* (e.g. @typed/app when the export is added). See httpapi-virtual-module-plugin spec.
|
|
26
|
+
*/
|
|
27
|
+
export interface HttpApiVirtualModulePluginOptions {
|
|
28
|
+
readonly [key: string]: unknown;
|
|
29
|
+
}
|
|
4
30
|
|
|
5
|
-
|
|
31
|
+
/** Options for vite-plugin-compression when compression is enabled. */
|
|
32
|
+
export type TypedViteCompressionOptions =
|
|
33
|
+
| boolean
|
|
34
|
+
| {
|
|
35
|
+
readonly algorithm?: "gzip" | "brotliCompress" | "deflate" | "deflateRaw";
|
|
36
|
+
readonly ext?: string;
|
|
37
|
+
readonly threshold?: number;
|
|
38
|
+
readonly [key: string]: unknown;
|
|
39
|
+
};
|
|
6
40
|
|
|
7
|
-
export
|
|
41
|
+
export interface TypedVitePluginOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Options for the router VM plugin from @typed/app.
|
|
44
|
+
*/
|
|
45
|
+
readonly routerVmOptions?: import("@typed/app").RouterVirtualModulePluginOptions;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Options for the HttpApi VM plugin from @typed/app. HttpApi VM plugin is always
|
|
49
|
+
* registered (router first, then HttpApi). Use this to customize its behavior.
|
|
50
|
+
*/
|
|
51
|
+
readonly apiVmOptions?: HttpApiVirtualModulePluginOptions;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Session factory for TypeInfo API. When not provided, a Language Service-backed
|
|
55
|
+
* session is auto-created from the project's tsconfig (evolves as files change).
|
|
56
|
+
* Override for custom session setup.
|
|
57
|
+
*/
|
|
58
|
+
readonly createTypeInfoApiSession?: CreateTypeInfoApiSession;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Enable tsconfig path resolution. Default true.
|
|
62
|
+
*/
|
|
63
|
+
readonly tsconfigPaths?: boolean | Record<string, unknown>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Enable bundle analyzer. Default: process.env.ANALYZE === '1'.
|
|
67
|
+
*/
|
|
68
|
+
readonly analyze?: boolean | { filename?: string; open?: boolean; template?: string };
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* When true, virtual module resolution errors are logged. Default true.
|
|
72
|
+
*/
|
|
73
|
+
readonly warnOnError?: boolean;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Enable Brotli compression for build. Default true.
|
|
77
|
+
* Set false to disable, or pass options to customize (algorithm, ext, threshold).
|
|
78
|
+
*/
|
|
79
|
+
readonly compression?: TypedViteCompressionOptions;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Optional dependency injection for createTypedViteResolver (e.g. for tests). */
|
|
83
|
+
export interface TypedViteResolverDependencies {
|
|
84
|
+
createHttpApiVirtualModulePlugin?: (
|
|
85
|
+
opts: HttpApiVirtualModulePluginOptions,
|
|
86
|
+
) => import("@typed/virtual-modules").VirtualModulePlugin;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Invariant: ALL @typed/app VM plugins are always registered. There are no optional
|
|
91
|
+
* or conditional app plugins. When adding a new VM plugin to @typed/app, add it here.
|
|
92
|
+
*/
|
|
93
|
+
export function createTypedViteResolver(
|
|
94
|
+
options: TypedVitePluginOptions = {},
|
|
95
|
+
dependencies?: TypedViteResolverDependencies,
|
|
96
|
+
): VirtualModuleResolver {
|
|
97
|
+
const httpApiFactory =
|
|
98
|
+
dependencies?.createHttpApiVirtualModulePlugin ??
|
|
99
|
+
createHttpApiVirtualModulePlugin;
|
|
100
|
+
const plugins: import("@typed/virtual-modules").VirtualModulePlugin[] = [
|
|
101
|
+
createRouterVirtualModulePlugin(options.routerVmOptions ?? {}),
|
|
102
|
+
httpApiFactory(options.apiVmOptions ?? {}),
|
|
103
|
+
];
|
|
104
|
+
return new PluginManager(plugins);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Returns Vite plugins: tsconfig paths, virtual modules (@typed/app), and optional bundle analyzer.
|
|
109
|
+
* Use as: `defineConfig({ plugins: typedVitePlugin() })`.
|
|
110
|
+
*
|
|
111
|
+
* When createTypeInfoApiSession is not provided, the plugin automatically creates a
|
|
112
|
+
* Language Service-backed session from the project's tsconfig. The type program evolves
|
|
113
|
+
* as files change during dev. Pass createTypeInfoApiSession to override.
|
|
114
|
+
*/
|
|
115
|
+
export function typedVitePlugin(options: TypedVitePluginOptions = {}): Plugin[] {
|
|
116
|
+
const resolver = createTypedViteResolver(options);
|
|
117
|
+
const analyze =
|
|
118
|
+
options.analyze ?? (process.env.ANALYZE === "1" ? true : false);
|
|
119
|
+
|
|
120
|
+
let createTypeInfoApiSession: CreateTypeInfoApiSession | undefined =
|
|
121
|
+
options.createTypeInfoApiSession;
|
|
122
|
+
|
|
123
|
+
if (createTypeInfoApiSession === undefined) {
|
|
124
|
+
try {
|
|
125
|
+
const manager = resolver as PluginManager;
|
|
126
|
+
const typeTargetSpecs = collectTypeTargetSpecsFromPlugins(manager.plugins);
|
|
127
|
+
createTypeInfoApiSession = createLanguageServiceSessionFactory({
|
|
128
|
+
ts,
|
|
129
|
+
projectRoot: process.cwd(),
|
|
130
|
+
typeTargetSpecs,
|
|
131
|
+
});
|
|
132
|
+
} catch {
|
|
133
|
+
// Graceful degradation: no session, plugins get noop TypeInfoApi
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const plugins: Plugin[] = [];
|
|
138
|
+
|
|
139
|
+
if (options.tsconfigPaths !== false) {
|
|
140
|
+
const pathsOpts =
|
|
141
|
+
typeof options.tsconfigPaths === "object" ? options.tsconfigPaths : {};
|
|
142
|
+
plugins.push(tsconfigPaths(pathsOpts) as Plugin);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
plugins.push(
|
|
146
|
+
virtualModulesVitePlugin({
|
|
147
|
+
resolver,
|
|
148
|
+
createTypeInfoApiSession,
|
|
149
|
+
warnOnError: options.warnOnError ?? true,
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
if (analyze) {
|
|
154
|
+
const vizOpts =
|
|
155
|
+
typeof analyze === "object"
|
|
156
|
+
? analyze
|
|
157
|
+
: { filename: "dist/stats.html", template: "treemap" as const };
|
|
158
|
+
plugins.push(
|
|
159
|
+
visualizer({
|
|
160
|
+
filename: vizOpts.filename ?? "dist/stats.html",
|
|
161
|
+
open: vizOpts.open ?? false,
|
|
162
|
+
template: (vizOpts.template as "treemap" | "sunburst" | "flamegraph" | "network") ?? "treemap",
|
|
163
|
+
}) as Plugin,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const compression = options.compression ?? true;
|
|
168
|
+
if (compression !== false) {
|
|
169
|
+
const compressionOpts =
|
|
170
|
+
typeof compression === "object"
|
|
171
|
+
? {
|
|
172
|
+
algorithm: "brotliCompress" as const,
|
|
173
|
+
ext: ".br",
|
|
174
|
+
threshold: 1024,
|
|
175
|
+
...compression,
|
|
176
|
+
}
|
|
177
|
+
: {
|
|
178
|
+
algorithm: "brotliCompress" as const,
|
|
179
|
+
ext: ".br",
|
|
180
|
+
threshold: 1024,
|
|
181
|
+
};
|
|
182
|
+
plugins.push(viteCompression(compressionOpts) as Plugin);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return plugins;
|
|
186
|
+
}
|
package/dist/cjs/constants.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,uBAAuB,CAAA"}
|
package/dist/cjs/constants.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,WAAW,GAAG,oBAAoB,CAAA"}
|
package/dist/cjs/index.d.ts
DELETED
package/dist/cjs/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,kBAAkB,CAAA;AAEpC,eAAe,KAAK,CAAA;AAEpB,cAAc,kBAAkB,CAAA;AAEhC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA"}
|