@foldkit/vite-plugin 0.26.0-canary.482593707a08 → 0.26.0-canary.9d701af6a751

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 CHANGED
@@ -65,6 +65,25 @@ With this set, the dev server converts HTML page requests to Web `Request` value
65
65
 
66
66
  Vite retains ownership of configured proxy routes before Foldkit handles application requests. Vite's `server.cors` option applies to Vite-owned source modules, assets, and HMR. It does not add headers to application responses or answer their preflights. Preflight ownership follows `Access-Control-Request-Method`, so a preflight for an application `POST` reaches `renderPage` even when its path looks like an asset. An `OPTIONS` request without both `Origin` and `Access-Control-Request-Method` is not a preflight and also reaches `renderPage`. Define application CORS in `renderPage`, where development and the deployed host share one policy. Vite's `allowedHosts` check runs before proxy and application handling, including `OPTIONS` and methods the Web `Request` API cannot represent.
67
67
 
68
+ ## Foldkit package resolution
69
+
70
+ Each module graph must load one Foldkit copy. The plugin configures Vite for that:
71
+
72
+ - `resolve.dedupe` lists `foldkit`, `@foldkit/ui`, and `@foldkit/devtools`, each only when it resolves from the application root.
73
+ - Server builds and the dev server's server render bundle those three packages, plus every installed package whose `dependencies` or `peerDependencies` include `foldkit` or an `@foldkit/*` package, such as `@foldkit/markdown`. In the dev server, these `ssr.noExternal` packages run through Vite's module runner instead of Node's own import. An explicit `ssr.external` entry still keeps a package external.
74
+ - The plugin finds these packages by crawling from the application's `package.json`. It follows the application's `dependencies` and `devDependencies`, then the `dependencies` of each package it bundles, plus the `devDependencies` of a bundled package that is a private workspace package.
75
+
76
+ A package the crawl does not reach stays external. For example: a peer the application does not declare, or a package reached only through a package that does not depend on Foldkit. Such a package loads a second Foldkit copy from `node_modules` at runtime. Declare it in the application's `package.json`, or add it to `ssr.noExternal`:
77
+
78
+ ```typescript
79
+ export default defineConfig({
80
+ plugins: [foldkit()],
81
+ ssr: { noExternal: ['foldkit-component-library'] },
82
+ })
83
+ ```
84
+
85
+ Vitest copies SSR `noExternal` into `server.deps.inline`. A Vitest config that includes `foldkit()` therefore also inlines the crawled packages in tests.
86
+
68
87
  ## Completed build metadata
69
88
 
70
89
  Deployment tools that run Vite in process can read `foldkit:build` through Vite's standard plugin `api` field. Await the full application build before reading:
@@ -1,12 +1,17 @@
1
+ import { type UserConfig } from 'vite';
1
2
  /** Tests whether an import refers to a package that must share Foldkit's
2
3
  * runtime instance.
3
4
  *
4
5
  * @internal
5
6
  */
6
7
  export declare const isFoldkitSingletonPackageSpecifier: (specifier: string) => boolean;
7
- /** Resolves the installed packages that must share Foldkit's runtime instance.
8
+ /** Finds the installed packages an application's server render must bundle
9
+ * and the Foldkit packages Vite must deduplicate.
8
10
  *
9
11
  * @internal
10
12
  */
11
- export declare const resolveInstalledFoldkitPackages: (root: string) => Array<string>;
13
+ export declare const crawlFoldkitPackages: (root: string, isBuild: boolean, viteUserConfig: UserConfig) => Promise<Readonly<{
14
+ dedupe: Array<string>;
15
+ ssrNoExternal: Array<string>;
16
+ }>>;
12
17
  //# sourceMappingURL=foldkitPackages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"foldkitPackages.d.ts","sourceRoot":"","sources":["../src/foldkitPackages.ts"],"names":[],"mappings":"AAUA;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,cAClC,MAAM,KAChB,OAKA,CAAA;AAEH;;;GAGG;AACH,eAAO,MAAM,+BAA+B,SACpC,MAAM,KACX,KAAK,CAAC,MAAM,CAmBd,CAAA"}
1
+ {"version":3,"file":"foldkitPackages.d.ts","sourceRoot":"","sources":["../src/foldkitPackages.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,UAAU,EAA0B,MAAM,MAAM,CAAA;AAS9D;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,cAClC,MAAM,KAChB,OAKA,CAAA;AA8CH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,SACzB,MAAM,WACH,OAAO,kBACA,UAAU,KACzB,OAAO,CACR,QAAQ,CAAC;IAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAAC,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAqClE,CAAA"}
@@ -1,6 +1,8 @@
1
- import { Array, Predicate } from 'effect';
2
- import { createRequire } from 'node:module';
1
+ import { Array, Option, Record, Schema, pipe } from 'effect';
2
+ import { realpathSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
+ import { searchForWorkspaceRoot } from 'vite';
5
+ import { crawlFrameworkPkgs, findDepPkgJsonPath } from 'vitefu';
4
6
  const FOLDKIT_SINGLETON_PACKAGES = [
5
7
  'foldkit',
6
8
  '@foldkit/ui',
@@ -12,26 +14,55 @@ const FOLDKIT_SINGLETON_PACKAGES = [
12
14
  * @internal
13
15
  */
14
16
  export const isFoldkitSingletonPackageSpecifier = (specifier) => Array.some(FOLDKIT_SINGLETON_PACKAGES, packageName => specifier === packageName || specifier.startsWith(`${packageName}/`));
15
- /** Resolves the installed packages that must share Foldkit's runtime instance.
17
+ const isFoldkitPackageName = (name) => name === 'foldkit' || name.startsWith('@foldkit/');
18
+ const decodeDependencyRecord = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.Unknown));
19
+ const dependencyNames = (field) => Option.match(decodeDependencyRecord(field), {
20
+ onNone: () => [],
21
+ onSome: Record.keys,
22
+ });
23
+ const dependsOnFoldkit = (packageJson) => pipe([packageJson['dependencies'], packageJson['peerDependencies']], Array.flatMap(dependencyNames), Array.some(isFoldkitPackageName));
24
+ const toRealPath = (path) => {
25
+ try {
26
+ return realpathSync(path);
27
+ }
28
+ catch {
29
+ return path;
30
+ }
31
+ };
32
+ const toCrawlRoot = (root, isPreservingSymlinks) => {
33
+ const absoluteRoot = resolve(root);
34
+ // NOTE: The crawl and the dedupe lookup must start from the root Vite
35
+ // resolves from: the path as given under resolve.preserveSymlinks, and its
36
+ // realpath otherwise. Any other root makes them walk a different directory
37
+ // chain than Vite's resolver.
38
+ if (isPreservingSymlinks) {
39
+ return absoluteRoot;
40
+ }
41
+ return toRealPath(absoluteRoot);
42
+ };
43
+ /** Finds the installed packages an application's server render must bundle
44
+ * and the Foldkit packages Vite must deduplicate.
16
45
  *
17
46
  * @internal
18
47
  */
19
- export const resolveInstalledFoldkitPackages = (root) => {
20
- // NOTE: Vite can supply a relative root before it resolves the config, while
21
- // createRequire requires an absolute path.
22
- const requireFromRoot = createRequire(resolve(root, 'noop.js'));
23
- return Array.filter(FOLDKIT_SINGLETON_PACKAGES, packageName => {
24
- try {
25
- requireFromRoot.resolve(packageName);
26
- return true;
27
- }
28
- catch (error) {
29
- // NOTE: optional ESM-only packages can throw
30
- // ERR_PACKAGE_PATH_NOT_EXPORTED even when installed. Only
31
- // MODULE_NOT_FOUND proves this consumer does not have the package.
32
- return !(error instanceof Error &&
33
- Predicate.hasProperty(error, 'code') &&
34
- error.code === 'MODULE_NOT_FOUND');
35
- }
48
+ export const crawlFoldkitPackages = async (root, isBuild, viteUserConfig) => {
49
+ const crawlRoot = toCrawlRoot(root, viteUserConfig.resolve?.preserveSymlinks ?? false);
50
+ // NOTE: vitefu realpaths each dependency's package.json before comparing it
51
+ // with workspaceRoot, including when Vite preserves symlinks. The workspace
52
+ // boundary must use the same form so private workspace packages are found.
53
+ const workspaceRoot = toRealPath(searchForWorkspaceRoot(crawlRoot));
54
+ const crawl = await crawlFrameworkPkgs({
55
+ root: crawlRoot,
56
+ workspaceRoot,
57
+ isBuild,
58
+ viteUserConfig,
59
+ isSemiFrameworkPkgByJson: dependsOnFoldkit,
36
60
  });
61
+ const ssrNoExternal = Array.dedupe([
62
+ ...FOLDKIT_SINGLETON_PACKAGES,
63
+ ...crawl.ssr.noExternal,
64
+ ]);
65
+ const maybeResolvableSingletons = await Promise.all(Array.map(FOLDKIT_SINGLETON_PACKAGES, async (packageName) => pipe(await findDepPkgJsonPath(packageName, crawlRoot), Option.fromUndefinedOr, Option.as(packageName))));
66
+ const dedupe = Array.getSomes(maybeResolvableSingletons);
67
+ return { dedupe, ssrNoExternal };
37
68
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAEV,MAAM,EAIP,MAAM,MAAM,CAAA;AAOb,OAAO,EAAE,KAAK,mBAAmB,EAAgB,MAAM,YAAY,CAAA;AAKnE,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,UAAU,CAAA;AAG7D,OAAO,EAAE,KAAK,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,YAAY,GACb,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC7D,OAAO,EACL,KAAK,2BAA2B,EAChC,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAE1B,6CAA6C;AAC7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAChC;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,gBAAgB,CAAC,GACzD,QAAQ,CAAC;QACP;;;;;;;;WAQG;QACH,KAAK,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAA;KACtC,CAAC,CAAA;IACJ;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAs6BF,eAAO,MAAM,OAAO,aAAa,oBAAoB,KAAQ,KAAK,CAAC,MAAM,CAuHxE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6CA,OAAO,KAAK,EAEV,MAAM,EAIP,MAAM,MAAM,CAAA;AAOb,OAAO,EAAE,KAAK,mBAAmB,EAAgB,MAAM,YAAY,CAAA;AAKnE,OAAO,EAAE,KAAK,iBAAiB,EAAc,MAAM,UAAU,CAAA;AAG7D,OAAO,EAAE,KAAK,eAAe,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,YAAY,GACb,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC7D,OAAO,EACL,KAAK,2BAA2B,EAChC,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,mBAAmB,CAAA;AAE1B,6CAA6C;AAC7C,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAChC;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,gBAAgB,CAAC,GACzD,QAAQ,CAAC;QACP;;;;;;;;WAQG;QACH,KAAK,CAAC,EAAE,OAAO,GAAG,mBAAmB,CAAA;KACtC,CAAC,CAAA;IACJ;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAC,CAAA;AAs6BF,eAAO,MAAM,OAAO,aAAa,oBAAoB,KAAQ,KAAK,CAAC,MAAM,CAyHxE,CAAA"}
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import * as NodePath from '@effect/platform-node/NodePath';
10
10
  import { foldkitBuild } from './build.js';
11
11
  import { foldkitBuildToken } from './buildToken.js';
12
12
  import { devToolsOverlayPlugin } from './devToolsOverlay.js';
13
- import { resolveInstalledFoldkitPackages } from './foldkitPackages.js';
13
+ import { crawlFoldkitPackages } from './foldkitPackages.js';
14
14
  import { publishRelayRecord, retireRelayRecord } from './relayRegistry.js';
15
15
  import { foldkitSsr } from './ssr.js';
16
16
  import { foldkitViewIdentity } from './viewIdentity.js';
@@ -544,22 +544,22 @@ export const foldkit = (options = {}) => {
544
544
  };
545
545
  const resolutionPlugin = {
546
546
  name: 'foldkit:resolution',
547
- config: userConfig => {
548
- const singletonPackages = resolveInstalledFoldkitPackages(userConfig.root ?? process.cwd());
547
+ config: async (userConfig, { command }) => {
548
+ const foldkitPackages = await crawlFoldkitPackages(userConfig.root ?? process.cwd(), command === 'build', userConfig);
549
549
  return {
550
550
  optimizeDeps: {
551
551
  exclude: ['foldkit'],
552
552
  },
553
553
  resolve: {
554
- dedupe: singletonPackages,
554
+ dedupe: foldkitPackages.dedupe,
555
555
  },
556
556
  ssr: {
557
- noExternal: singletonPackages,
557
+ noExternal: foldkitPackages.ssrNoExternal,
558
558
  },
559
559
  environments: {
560
560
  ssr: {
561
561
  resolve: {
562
- noExternal: singletonPackages,
562
+ noExternal: foldkitPackages.ssrNoExternal,
563
563
  },
564
564
  },
565
565
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foldkit/vite-plugin",
3
- "version": "0.26.0-canary.482593707a08",
3
+ "version": "0.26.0-canary.9d701af6a751",
4
4
  "description": "Vite plugin for Foldkit with state-preserving live reload",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,13 +17,14 @@
17
17
  ],
18
18
  "peerDependencies": {
19
19
  "effect": "4.0.0",
20
- "foldkit": "0.165.0-canary.482593707a08",
20
+ "foldkit": "0.165.0-canary.9d701af6a751",
21
21
  "vite": "^8.0.0"
22
22
  },
23
23
  "dependencies": {
24
24
  "@effect/platform-node": "4.0.0",
25
25
  "@effect/platform-node-shared": "4.0.0",
26
26
  "magic-string": "^1.4.2",
27
+ "vitefu": "^1.1.3",
27
28
  "ws": "^8.21.3"
28
29
  },
29
30
  "devDependencies": {
@@ -37,7 +38,7 @@
37
38
  "vite": "^8.3.1",
38
39
  "vite-host": "npm:vite@8.2.1",
39
40
  "vitest": "^4.1.11",
40
- "foldkit": "0.165.0-canary.482593707a08"
41
+ "foldkit": "0.165.0-canary.9d701af6a751"
41
42
  },
42
43
  "keywords": [
43
44
  "vite",