auto-server-split 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,134 @@
1
+ // src/analysis/clientPackages.ts
2
+ var BUILTIN_CLIENT_ONLY_PACKAGES = [
3
+ "framer-motion",
4
+ "motion",
5
+ "react-hook-form",
6
+ "@hookform/*",
7
+ "swiper",
8
+ "recharts",
9
+ "chart.js",
10
+ "react-chartjs-2",
11
+ "@tanstack/react-query",
12
+ "@tanstack/react-table",
13
+ "@tanstack/react-virtual",
14
+ "@tanstack/react-form",
15
+ "zustand",
16
+ "jotai",
17
+ "valtio",
18
+ "react-redux",
19
+ "@reduxjs/toolkit",
20
+ "mobx-react",
21
+ "mobx-react-lite",
22
+ "swr",
23
+ "@apollo/client",
24
+ "urql",
25
+ "react-hot-toast",
26
+ "react-toastify",
27
+ "sonner",
28
+ "@radix-ui/*",
29
+ "@headlessui/react",
30
+ "@floating-ui/react",
31
+ "@floating-ui/react-dom",
32
+ "@ariakit/react",
33
+ "react-aria",
34
+ "react-aria-components",
35
+ "react-select",
36
+ "react-dnd",
37
+ "react-dnd-html5-backend",
38
+ "@dnd-kit/*",
39
+ "react-beautiful-dnd",
40
+ "@hello-pangea/dnd",
41
+ "react-spring",
42
+ "@react-spring/*",
43
+ "@use-gesture/react",
44
+ "embla-carousel-react",
45
+ "keen-slider",
46
+ "react-slick",
47
+ "gsap",
48
+ "@gsap/react",
49
+ "lottie-react",
50
+ "@lottiefiles/react-lottie-player",
51
+ "react-confetti",
52
+ "react-countup",
53
+ "@react-three/fiber",
54
+ "@react-three/drei",
55
+ "three",
56
+ "react-leaflet",
57
+ "leaflet",
58
+ "mapbox-gl",
59
+ "react-map-gl",
60
+ "maplibre-gl",
61
+ "react-day-picker",
62
+ "react-datepicker",
63
+ "react-colorful",
64
+ "react-color",
65
+ "cmdk",
66
+ "vaul",
67
+ "react-dropzone",
68
+ "react-player",
69
+ "react-quill",
70
+ "@tiptap/react",
71
+ "slate-react",
72
+ "@lexical/react",
73
+ "react-intersection-observer",
74
+ "react-use",
75
+ "usehooks-ts",
76
+ "@uidotdev/usehooks",
77
+ "react-hotkeys-hook",
78
+ "react-modal",
79
+ "react-transition-group",
80
+ "react-window",
81
+ "react-virtualized",
82
+ "react-virtuoso",
83
+ "next-themes",
84
+ "@mantine/*",
85
+ "@chakra-ui/*",
86
+ "antd",
87
+ "@mui/*",
88
+ "react-bootstrap",
89
+ "@nextui-org/*",
90
+ "@heroui/*",
91
+ "react-i18next",
92
+ "@clerk/nextjs",
93
+ "next-auth/react",
94
+ "@supabase/auth-helpers-react",
95
+ "posthog-js",
96
+ "react-ga4"
97
+ ];
98
+ var CSS_IN_JS_PACKAGES = [
99
+ "styled-components",
100
+ "@emotion/react",
101
+ "@emotion/styled",
102
+ "@emotion/css",
103
+ "@emotion/core",
104
+ "@stitches/react",
105
+ "goober",
106
+ "styled-jsx",
107
+ "styled-jsx/css",
108
+ "theme-ui",
109
+ "jss",
110
+ "react-jss",
111
+ "@linaria/react",
112
+ "styled-system"
113
+ ];
114
+ var BUILTIN_SERVER_SAFE_PACKAGES = ["@heroicons/react", "@radix-ui/react-icons"];
115
+ function packageNameOf(specifier) {
116
+ const parts = specifier.split("/");
117
+ return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0] ?? specifier;
118
+ }
119
+ function matchesPackageList(specifier, list) {
120
+ const name = packageNameOf(specifier);
121
+ return list.some((entry) => {
122
+ if (entry.endsWith("/*")) return name.startsWith(entry.slice(0, -1));
123
+ return entry === name || entry === specifier || specifier.startsWith(`${entry}/`);
124
+ });
125
+ }
126
+
127
+ export {
128
+ BUILTIN_CLIENT_ONLY_PACKAGES,
129
+ CSS_IN_JS_PACKAGES,
130
+ BUILTIN_SERVER_SAFE_PACKAGES,
131
+ packageNameOf,
132
+ matchesPackageList
133
+ };
134
+ //# sourceMappingURL=chunk-YCSBOPV3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/analysis/clientPackages.ts"],"sourcesContent":["/**\n * Packages whose exports only work in the browser or rely on client React APIs (state, effects,\n * context). Anything imported from these is client-only. Entries ending in `/*` match a scope.\n */\nexport const BUILTIN_CLIENT_ONLY_PACKAGES = [\n \"framer-motion\",\n \"motion\",\n \"react-hook-form\",\n \"@hookform/*\",\n \"swiper\",\n \"recharts\",\n \"chart.js\",\n \"react-chartjs-2\",\n \"@tanstack/react-query\",\n \"@tanstack/react-table\",\n \"@tanstack/react-virtual\",\n \"@tanstack/react-form\",\n \"zustand\",\n \"jotai\",\n \"valtio\",\n \"react-redux\",\n \"@reduxjs/toolkit\",\n \"mobx-react\",\n \"mobx-react-lite\",\n \"swr\",\n \"@apollo/client\",\n \"urql\",\n \"react-hot-toast\",\n \"react-toastify\",\n \"sonner\",\n \"@radix-ui/*\",\n \"@headlessui/react\",\n \"@floating-ui/react\",\n \"@floating-ui/react-dom\",\n \"@ariakit/react\",\n \"react-aria\",\n \"react-aria-components\",\n \"react-select\",\n \"react-dnd\",\n \"react-dnd-html5-backend\",\n \"@dnd-kit/*\",\n \"react-beautiful-dnd\",\n \"@hello-pangea/dnd\",\n \"react-spring\",\n \"@react-spring/*\",\n \"@use-gesture/react\",\n \"embla-carousel-react\",\n \"keen-slider\",\n \"react-slick\",\n \"gsap\",\n \"@gsap/react\",\n \"lottie-react\",\n \"@lottiefiles/react-lottie-player\",\n \"react-confetti\",\n \"react-countup\",\n \"@react-three/fiber\",\n \"@react-three/drei\",\n \"three\",\n \"react-leaflet\",\n \"leaflet\",\n \"mapbox-gl\",\n \"react-map-gl\",\n \"maplibre-gl\",\n \"react-day-picker\",\n \"react-datepicker\",\n \"react-colorful\",\n \"react-color\",\n \"cmdk\",\n \"vaul\",\n \"react-dropzone\",\n \"react-player\",\n \"react-quill\",\n \"@tiptap/react\",\n \"slate-react\",\n \"@lexical/react\",\n \"react-intersection-observer\",\n \"react-use\",\n \"usehooks-ts\",\n \"@uidotdev/usehooks\",\n \"react-hotkeys-hook\",\n \"react-modal\",\n \"react-transition-group\",\n \"react-window\",\n \"react-virtualized\",\n \"react-virtuoso\",\n \"next-themes\",\n \"@mantine/*\",\n \"@chakra-ui/*\",\n \"antd\",\n \"@mui/*\",\n \"react-bootstrap\",\n \"@nextui-org/*\",\n \"@heroui/*\",\n \"react-i18next\",\n \"@clerk/nextjs\",\n \"next-auth/react\",\n \"@supabase/auth-helpers-react\",\n \"posthog-js\",\n \"react-ga4\",\n];\n\n/** Runtime CSS-in-JS libraries. A file that uses one is always skipped. */\nexport const CSS_IN_JS_PACKAGES = [\n \"styled-components\",\n \"@emotion/react\",\n \"@emotion/styled\",\n \"@emotion/css\",\n \"@emotion/core\",\n \"@stitches/react\",\n \"goober\",\n \"styled-jsx\",\n \"styled-jsx/css\",\n \"theme-ui\",\n \"jss\",\n \"react-jss\",\n \"@linaria/react\",\n \"styled-system\",\n];\n\n/**\n * React-dependent packages verified to render correctly as Server Components (pure function\n * components, no context, state or effects). Everything else that depends on React and has no\n * \"use client\" entry is treated as client-only.\n */\nexport const BUILTIN_SERVER_SAFE_PACKAGES = [\"@heroicons/react\", \"@radix-ui/react-icons\"];\n\nexport function packageNameOf(specifier: string): string {\n const parts = specifier.split(\"/\");\n return specifier.startsWith(\"@\") ? parts.slice(0, 2).join(\"/\") : (parts[0] ?? specifier);\n}\n\n/** Matches a specifier against a package list (exact name, subpath, or `@scope/*`). */\nexport function matchesPackageList(specifier: string, list: readonly string[]): boolean {\n const name = packageNameOf(specifier);\n return list.some((entry) => {\n if (entry.endsWith(\"/*\")) return name.startsWith(entry.slice(0, -1));\n return entry === name || entry === specifier || specifier.startsWith(`${entry}/`);\n });\n}\n"],"mappings":";AAIO,IAAM,+BAA+B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,qBAAqB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,IAAM,+BAA+B,CAAC,oBAAoB,uBAAuB;AAEjF,SAAS,cAAc,WAA2B;AACvD,QAAM,QAAQ,UAAU,MAAM,GAAG;AACjC,SAAO,UAAU,WAAW,GAAG,IAAI,MAAM,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,IAAK,MAAM,CAAC,KAAK;AAChF;AAGO,SAAS,mBAAmB,WAAmB,MAAkC;AACtF,QAAM,OAAO,cAAc,SAAS;AACpC,SAAO,KAAK,KAAK,CAAC,UAAU;AAC1B,QAAI,MAAM,SAAS,IAAI,EAAG,QAAO,KAAK,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AACnE,WAAO,UAAU,QAAQ,UAAU,aAAa,UAAU,WAAW,GAAG,KAAK,GAAG;AAAA,EAClF,CAAC;AACH;","names":[]}
@@ -0,0 +1,155 @@
1
+ import {
2
+ packageNameOf
3
+ } from "./chunk-YCSBOPV3.js";
4
+
5
+ // src/measure/bundleSize.ts
6
+ import { createHash } from "crypto";
7
+ import path from "path";
8
+ import { gzipSync } from "zlib";
9
+ import { buildSync } from "esbuild";
10
+ var CACHE_LIMIT = 500;
11
+ var EMPTY_LOADERS = [
12
+ ".module.css",
13
+ ".module.scss",
14
+ ".module.sass",
15
+ ".module.less",
16
+ ".css",
17
+ ".scss",
18
+ ".sass",
19
+ ".less",
20
+ ".styl",
21
+ ".svg",
22
+ ".png",
23
+ ".jpg",
24
+ ".jpeg",
25
+ ".gif",
26
+ ".webp",
27
+ ".avif",
28
+ ".ico",
29
+ ".woff",
30
+ ".woff2",
31
+ ".ttf",
32
+ ".otf",
33
+ ".mp4",
34
+ ".webm",
35
+ ".mp3",
36
+ ".wav"
37
+ ];
38
+ var BundleMeasurer = class {
39
+ constructor(root, graph, tsconfig) {
40
+ this.root = root;
41
+ this.graph = graph;
42
+ this.configFile = tsconfig;
43
+ }
44
+ root;
45
+ graph;
46
+ cache = /* @__PURE__ */ new Map();
47
+ configFile;
48
+ bundle(entry) {
49
+ const key = createHash("sha256").update(entry.file).update("\0").update(entry.contents).digest("hex");
50
+ const cached = this.cache.get(key);
51
+ if (cached) return cached;
52
+ const ext = path.extname(entry.file).slice(1);
53
+ const loader = ["tsx", "ts", "jsx", "js"].includes(ext) ? ext : "tsx";
54
+ const options = {
55
+ stdin: {
56
+ contents: entry.contents,
57
+ resolveDir: path.dirname(entry.file),
58
+ sourcefile: entry.file,
59
+ loader
60
+ },
61
+ absWorkingDir: this.root,
62
+ platform: "browser",
63
+ bundle: true,
64
+ minify: true,
65
+ write: false,
66
+ metafile: true,
67
+ format: "esm",
68
+ jsx: "automatic",
69
+ logLevel: "silent",
70
+ define: { "process.env.NODE_ENV": '"production"' },
71
+ external: ["react", "react/*", "react-dom", "react-dom/*", "next", "next/*"],
72
+ loader: Object.fromEntries(EMPTY_LOADERS.map((e) => [e, "empty"])),
73
+ ...this.configFile ? { tsconfig: this.configFile } : {}
74
+ };
75
+ let stats;
76
+ try {
77
+ const result = buildSync(options);
78
+ const output = result.outputFiles?.find((f) => f.path.endsWith(".js")) ?? result.outputFiles?.[0];
79
+ stats = output ? {
80
+ gzip: gzipSync(output.contents).length,
81
+ minified: output.contents.length,
82
+ inputs: inputBytes(result.metafile)
83
+ } : { error: "esbuild produced no output" };
84
+ } catch (error) {
85
+ stats = { error: describeBuildError(error) };
86
+ }
87
+ if (this.cache.size >= CACHE_LIMIT) this.cache.delete(this.cache.keys().next().value);
88
+ this.cache.set(key, stats);
89
+ return stats;
90
+ }
91
+ /**
92
+ * `isolated` = before − after. `conservative` also leaves out code that other client modules still
93
+ * import (it ships anyway); gzip is apportioned by each input's share of the minified output.
94
+ */
95
+ measure(file, originalText, islandFile, islandText) {
96
+ const before = this.bundle({ file, contents: originalText });
97
+ if ("error" in before) return before;
98
+ const after = this.bundle({ file: islandFile, contents: islandText });
99
+ if ("error" in after) return after;
100
+ const isolated = Math.max(0, before.gzip - after.gzip);
101
+ const stillShipped = this.stillShippedElsewhere(file);
102
+ let sharedBytes = 0;
103
+ for (const [input, bytes] of before.inputs) {
104
+ if (after.inputs.has(input)) continue;
105
+ if (input === "<stdin>") continue;
106
+ if (stillShipped(input)) sharedBytes += bytes;
107
+ }
108
+ const ratio = before.minified > 0 ? before.gzip / before.minified : 0;
109
+ const conservative = Math.max(0, Math.round(isolated - sharedBytes * ratio));
110
+ return { bytesSaved: { isolated, conservative: Math.min(conservative, isolated) } };
111
+ }
112
+ /** Packages and project modules imported by some other client module (they keep shipping). */
113
+ stillShippedElsewhere(file) {
114
+ const packages = /* @__PURE__ */ new Set();
115
+ const localFiles = /* @__PURE__ */ new Set();
116
+ for (const node of this.graph.nodes.values()) {
117
+ if (node.file === file || !this.graph.isClient(node.file)) continue;
118
+ for (const edge of node.edges) {
119
+ if (edge.kind === "type") continue;
120
+ if (edge.to) {
121
+ if (edge.to !== file) localFiles.add(edge.to);
122
+ } else if (edge.external) {
123
+ packages.add(edge.external.packageName);
124
+ }
125
+ }
126
+ }
127
+ return (input) => {
128
+ const marker = "node_modules/";
129
+ const i = input.lastIndexOf(marker);
130
+ if (i !== -1) return packages.has(packageNameOf(input.slice(i + marker.length)));
131
+ return localFiles.has(path.resolve(this.root, input));
132
+ };
133
+ }
134
+ };
135
+ function describeBuildError(error) {
136
+ const first = error.errors?.[0];
137
+ if (!first) return `esbuild: ${error instanceof Error ? error.message : String(error)}`;
138
+ const loc = first.location ? `${first.location.file}:${first.location.line}:${first.location.column + 1}: ` : "";
139
+ return `esbuild: ${loc}${first.text}`;
140
+ }
141
+ function inputBytes(metafile) {
142
+ const out = /* @__PURE__ */ new Map();
143
+ if (!metafile) return out;
144
+ for (const output of Object.values(metafile.outputs)) {
145
+ for (const [input, info] of Object.entries(output.inputs)) {
146
+ out.set(input, (out.get(input) ?? 0) + info.bytesInOutput);
147
+ }
148
+ }
149
+ return out;
150
+ }
151
+
152
+ export {
153
+ BundleMeasurer
154
+ };
155
+ //# sourceMappingURL=chunk-YQKO5FJM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/measure/bundleSize.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport { gzipSync } from \"node:zlib\";\nimport type { BuildOptions, Message, Metafile } from \"esbuild\";\nimport { buildSync } from \"esbuild\";\nimport { packageNameOf } from \"../analysis/clientPackages.js\";\nimport type { ModuleGraph } from \"../project/moduleGraph.js\";\n\ntype BytesSaved = { isolated: number; conservative: number };\n\n/** The saving, or why it couldn't be measured (esbuild couldn't bundle one of the files). */\nexport type Measurement = { bytesSaved: BytesSaved } | { error: string };\n\ntype BundleStats = { gzip: number; minified: number; inputs: Map<string, number> };\n\n/** Bundles kept per run; an editor's ESLint process measures every edit, so this is bounded. */\nconst CACHE_LIMIT = 500;\n\n/**\n * Imports that ship no client JS (or not enough to matter for the estimate). esbuild matches the\n * longest extension, and its built-in `.module.css` → `local-css` loader would otherwise win over\n * `.css` and fail (\"without an output path configured\"), so CSS Modules are listed explicitly.\n */\nconst EMPTY_LOADERS = [\n \".module.css\",\n \".module.scss\",\n \".module.sass\",\n \".module.less\",\n \".css\",\n \".scss\",\n \".sass\",\n \".less\",\n \".styl\",\n \".svg\",\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".webp\",\n \".avif\",\n \".ico\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".otf\",\n \".mp4\",\n \".webm\",\n \".mp3\",\n \".wav\",\n];\n\n/**\n * Estimates the client JavaScript saved by a split: bundle the original client file and\n * the generated island file with esbuild (react, react-dom and next external), and compare gzip sizes.\n * Synchronous, so the CLI and the ESLint rule (which can't await) share it.\n */\nexport class BundleMeasurer {\n private readonly cache = new Map<string, BundleStats | { error: string }>();\n private readonly configFile: string | undefined;\n\n constructor(\n private readonly root: string,\n private readonly graph: ModuleGraph,\n tsconfig?: string,\n ) {\n this.configFile = tsconfig;\n }\n\n private bundle(entry: { file: string; contents: string }): BundleStats | { error: string } {\n const key = createHash(\"sha256\")\n .update(entry.file)\n .update(\"\\0\")\n .update(entry.contents)\n .digest(\"hex\");\n const cached = this.cache.get(key);\n if (cached) return cached;\n const ext = path.extname(entry.file).slice(1);\n const loader = ([\"tsx\", \"ts\", \"jsx\", \"js\"].includes(ext) ? ext : \"tsx\") as\n \"tsx\" | \"ts\" | \"jsx\" | \"js\";\n const options: BuildOptions = {\n stdin: {\n contents: entry.contents,\n resolveDir: path.dirname(entry.file),\n sourcefile: entry.file,\n loader,\n },\n absWorkingDir: this.root,\n platform: \"browser\",\n bundle: true,\n minify: true,\n write: false,\n metafile: true,\n format: \"esm\",\n jsx: \"automatic\",\n logLevel: \"silent\",\n define: { \"process.env.NODE_ENV\": '\"production\"' },\n external: [\"react\", \"react/*\", \"react-dom\", \"react-dom/*\", \"next\", \"next/*\"],\n loader: Object.fromEntries(EMPTY_LOADERS.map((e) => [e, \"empty\" as const])),\n ...(this.configFile ? { tsconfig: this.configFile } : {}),\n };\n let stats: BundleStats | { error: string };\n try {\n const result = buildSync(options);\n const output =\n result.outputFiles?.find((f) => f.path.endsWith(\".js\")) ?? result.outputFiles?.[0];\n stats = output\n ? {\n gzip: gzipSync(output.contents).length,\n minified: output.contents.length,\n inputs: inputBytes(result.metafile),\n }\n : { error: \"esbuild produced no output\" };\n } catch (error) {\n stats = { error: describeBuildError(error) };\n }\n if (this.cache.size >= CACHE_LIMIT) this.cache.delete(this.cache.keys().next().value as string);\n this.cache.set(key, stats);\n return stats;\n }\n\n /**\n * `isolated` = before − after. `conservative` also leaves out code that other client modules still\n * import (it ships anyway); gzip is apportioned by each input's share of the minified output.\n */\n measure(file: string, originalText: string, islandFile: string, islandText: string): Measurement {\n const before = this.bundle({ file, contents: originalText });\n if (\"error\" in before) return before;\n const after = this.bundle({ file: islandFile, contents: islandText });\n if (\"error\" in after) return after;\n const isolated = Math.max(0, before.gzip - after.gzip);\n\n const stillShipped = this.stillShippedElsewhere(file);\n let sharedBytes = 0;\n for (const [input, bytes] of before.inputs) {\n if (after.inputs.has(input)) continue;\n if (input === \"<stdin>\") continue;\n if (stillShipped(input)) sharedBytes += bytes;\n }\n const ratio = before.minified > 0 ? before.gzip / before.minified : 0;\n const conservative = Math.max(0, Math.round(isolated - sharedBytes * ratio));\n return { bytesSaved: { isolated, conservative: Math.min(conservative, isolated) } };\n }\n\n /** Packages and project modules imported by some other client module (they keep shipping). */\n private stillShippedElsewhere(file: string): (input: string) => boolean {\n const packages = new Set<string>();\n const localFiles = new Set<string>();\n for (const node of this.graph.nodes.values()) {\n if (node.file === file || !this.graph.isClient(node.file)) continue;\n for (const edge of node.edges) {\n if (edge.kind === \"type\") continue;\n if (edge.to) {\n if (edge.to !== file) localFiles.add(edge.to);\n } else if (edge.external) {\n packages.add(edge.external.packageName);\n }\n }\n }\n return (input: string) => {\n const marker = \"node_modules/\";\n const i = input.lastIndexOf(marker);\n if (i !== -1) return packages.has(packageNameOf(input.slice(i + marker.length)));\n return localFiles.has(path.resolve(this.root, input));\n };\n }\n}\n\n/** \"esbuild: components/Card.tsx:3:20: Could not resolve \"virtual-fmt\"\" */\nfunction describeBuildError(error: unknown): string {\n const first = (error as { errors?: Message[] }).errors?.[0];\n if (!first) return `esbuild: ${error instanceof Error ? error.message : String(error)}`;\n const loc = first.location\n ? `${first.location.file}:${first.location.line}:${first.location.column + 1}: `\n : \"\";\n return `esbuild: ${loc}${first.text}`;\n}\n\nfunction inputBytes(metafile: Metafile | undefined): Map<string, number> {\n const out = new Map<string, number>();\n if (!metafile) return out;\n for (const output of Object.values(metafile.outputs)) {\n for (const [input, info] of Object.entries(output.inputs)) {\n out.set(input, (out.get(input) ?? 0) + info.bytesInOutput);\n }\n }\n return out;\n}\n"],"mappings":"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","names":[]}
package/dist/cli.js ADDED
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ ConfigError,
4
+ SKIP_TITLES,
5
+ formatBytes,
6
+ runSplit
7
+ } from "./chunk-6OAWHATP.js";
8
+ import "./chunk-YCSBOPV3.js";
9
+
10
+ // src/cli.ts
11
+ import { execFileSync } from "child_process";
12
+ import { existsSync, mkdirSync, realpathSync, writeFileSync } from "fs";
13
+ import path from "path";
14
+ import { defineCommand, runMain } from "citty";
15
+ import pc from "picocolors";
16
+ var EXIT_OK = 0;
17
+ var EXIT_ERROR = 1;
18
+ var EXIT_SPLITS_AVAILABLE = 2;
19
+ function colorDiff(diff) {
20
+ return diff.split("\n").map((line) => {
21
+ if (line.startsWith("+++") || line.startsWith("---") || line.startsWith("==="))
22
+ return pc.bold(line);
23
+ if (line.startsWith("+")) return pc.green(line);
24
+ if (line.startsWith("-")) return pc.red(line);
25
+ if (line.startsWith("@@")) return pc.cyan(line);
26
+ return line;
27
+ }).join("\n");
28
+ }
29
+ function describeSplit(file) {
30
+ const islands = file.islands.map(
31
+ (i) => `${i.name} <${i.rootTag}>${i.props.length ? ` (props: ${i.props.join(", ")})` : ""}`
32
+ );
33
+ const saved = file.bytesSaved ? ` \xB7 ~${formatBytes(file.bytesSaved.conservative)} gzip saved (estimated)` : file.measureError ? ` \xB7 ${pc.yellow("saving not measured")} (${file.measureError})` : "";
34
+ return `${pc.green("\u2714 split")} ${pc.bold(file.file)} \xB7 ${file.islands.length} island${file.islands.length === 1 ? "" : "s"} \xB7 ${Math.round(file.staticRatio * 100)}% static${saved}
35
+ ${islands.join("\n ")}`;
36
+ }
37
+ function describeSkip(file, verbose) {
38
+ const s = file.skipped;
39
+ if (!s) return "";
40
+ const loc = s.location ? `:${s.location.line}:${s.location.column}` : "";
41
+ let message = s.message;
42
+ if (s.code === "internal-error" && !verbose) message = message.split("\n")[0] ?? message;
43
+ const color = s.code === "internal-error" || s.code === "self-check-failed" ? pc.red : pc.yellow;
44
+ return `${color("\u2013 skipped")} ${file.file}${loc} ${pc.dim(`[${s.code}]`)} ${message}`;
45
+ }
46
+ function dirtyFiles(cwd, files) {
47
+ if (files.length === 0) return [];
48
+ try {
49
+ const out = execFileSync(
50
+ "git",
51
+ ["status", "--porcelain", "--ignored", "--untracked-files=all", "--", ...files],
52
+ {
53
+ cwd,
54
+ encoding: "utf8",
55
+ stdio: ["ignore", "pipe", "ignore"]
56
+ }
57
+ );
58
+ return out.split("\n").filter(Boolean).map(
59
+ (l) => `${l.slice(3)}${l.startsWith("!!") ? " (ignored by git)" : l.startsWith("??") ? " (untracked)" : ""}`
60
+ );
61
+ } catch {
62
+ return void 0;
63
+ }
64
+ }
65
+ var main = defineCommand({
66
+ meta: {
67
+ name: "auto-server-split",
68
+ version: true ? "1.0.0" : "0.0.0-dev",
69
+ description: 'Split partly-interactive "use client" components into a Server Component plus small client islands.'
70
+ },
71
+ args: {
72
+ paths: {
73
+ type: "positional",
74
+ required: false,
75
+ description: "Files or directories (default: the config `include` globs)"
76
+ },
77
+ "dry-run": { type: "boolean", description: "Show the diff without writing anything (default)" },
78
+ write: { type: "boolean", description: "Apply the changes" },
79
+ check: {
80
+ type: "boolean",
81
+ description: "CI mode: exit 2 if any split is available, change nothing"
82
+ },
83
+ interactive: {
84
+ type: "boolean",
85
+ description: "Review each proposed split ([y]es/[n]o/[r]ename/[d]iff/[q]uit)"
86
+ },
87
+ json: { type: "boolean", description: "Print a machine-readable report" },
88
+ config: {
89
+ type: "string",
90
+ description: "Config file (default: auto-split.config.* in the project root)"
91
+ },
92
+ root: { type: "string", description: "Project root (where tsconfig.json lives; default: cwd)" },
93
+ "allow-dirty": {
94
+ type: "boolean",
95
+ description: "Allow --write on files with uncommitted git changes"
96
+ },
97
+ verbose: { type: "boolean", description: "Show stack traces for internal errors" },
98
+ measure: {
99
+ type: "boolean",
100
+ default: true,
101
+ description: "Measure bundle sizes with esbuild (--no-measure to skip)"
102
+ }
103
+ },
104
+ async run({ args }) {
105
+ const cwd = process.cwd();
106
+ const rootArg = path.resolve(cwd, args.root ?? ".");
107
+ if (!existsSync(rootArg)) {
108
+ console.error(pc.red(`auto-server-split: ${args.root ?? "."} does not exist`));
109
+ process.exitCode = EXIT_ERROR;
110
+ return;
111
+ }
112
+ const root = realpathSync(rootArg);
113
+ const positional = args._.filter(Boolean);
114
+ for (const p of positional) {
115
+ if (!existsSync(path.resolve(cwd, p))) {
116
+ console.error(pc.red(`auto-server-split: ${p} does not exist`));
117
+ process.exitCode = EXIT_ERROR;
118
+ return;
119
+ }
120
+ }
121
+ const paths = positional.map(
122
+ (p) => path.relative(root, realpathSync(path.resolve(cwd, p))) || "."
123
+ );
124
+ if (args.interactive && (args["dry-run"] || args.json || args.check)) {
125
+ console.error(
126
+ pc.red(
127
+ "auto-server-split: --interactive can't be combined with --dry-run, --json or --check"
128
+ )
129
+ );
130
+ process.exitCode = EXIT_ERROR;
131
+ return;
132
+ }
133
+ if (args.interactive && !process.stdin.isTTY) {
134
+ console.error(pc.red("auto-server-split: --interactive needs a terminal"));
135
+ process.exitCode = EXIT_ERROR;
136
+ return;
137
+ }
138
+ const modes = [args["dry-run"], args.write, args.check].filter(Boolean).length;
139
+ if (modes > 1) {
140
+ console.error(pc.red("auto-server-split: use only one of --dry-run, --write, --check"));
141
+ process.exitCode = EXIT_ERROR;
142
+ return;
143
+ }
144
+ const verbose = Boolean(args.verbose);
145
+ const json = Boolean(args.json);
146
+ const log = (line = "") => {
147
+ if (!json) console.log(line);
148
+ };
149
+ let report;
150
+ try {
151
+ report = await runSplit({
152
+ root,
153
+ ...paths.length > 0 ? { paths } : {},
154
+ write: false,
155
+ ...args.config ? { configFile: args.config } : {},
156
+ measure: args.measure !== false,
157
+ onFile: (file) => {
158
+ if (json || args.interactive) return;
159
+ if (file.status === "split") {
160
+ log(describeSplit(file));
161
+ if (!args.check) log(colorDiff(file.diff));
162
+ } else if (file.skipped) {
163
+ log(describeSkip(file, verbose));
164
+ }
165
+ }
166
+ });
167
+ } catch (error) {
168
+ if (error instanceof ConfigError)
169
+ console.error(pc.red(`auto-server-split: invalid config
170
+ ${error.message}`));
171
+ else console.error(pc.red(`auto-server-split: ${error.stack ?? String(error)}`));
172
+ process.exitCode = EXIT_ERROR;
173
+ return;
174
+ }
175
+ let files = report.files;
176
+ let toWrite = args.write ? files.filter((f) => f.status === "split") : [];
177
+ if (args.interactive) {
178
+ const { reviewInteractively } = await import("./interactive-JF66CJGV.js");
179
+ toWrite = await reviewInteractively(files, report.rerun, {
180
+ input: process.stdin,
181
+ output: process.stdout,
182
+ colorDiff,
183
+ describe: describeSplit
184
+ });
185
+ const acceptedByFile = new Map(toWrite.map((f) => [f.file, f]));
186
+ files = files.map((f) => acceptedByFile.get(f.file) ?? f);
187
+ }
188
+ const splits = files.filter((f) => f.status === "split");
189
+ if (toWrite.length > 0) {
190
+ const touched = toWrite.flatMap((f) => Object.keys(f.outputs));
191
+ if (!args["allow-dirty"]) {
192
+ const dirty = dirtyFiles(
193
+ root,
194
+ touched.filter((t) => existsSync(path.join(root, t)))
195
+ );
196
+ if (dirty === void 0) {
197
+ console.error(
198
+ pc.red(
199
+ "auto-server-split: can't read git status (not a git repository?). Commit your work or pass --allow-dirty."
200
+ )
201
+ );
202
+ process.exitCode = EXIT_ERROR;
203
+ return;
204
+ }
205
+ if (dirty.length > 0) {
206
+ console.error(
207
+ pc.red(
208
+ `auto-server-split: refusing to write; these files have uncommitted changes (or aren't tracked by git):
209
+ ${dirty.join("\n ")}
210
+ Commit them or pass --allow-dirty.`
211
+ )
212
+ );
213
+ process.exitCode = EXIT_ERROR;
214
+ return;
215
+ }
216
+ }
217
+ for (const file of toWrite) {
218
+ for (const [rel, content] of Object.entries(file.outputs)) {
219
+ const abs = path.join(root, rel);
220
+ mkdirSync(path.dirname(abs), { recursive: true });
221
+ writeFileSync(abs, content);
222
+ }
223
+ }
224
+ }
225
+ if (json) {
226
+ console.log(JSON.stringify({ files }, null, 2));
227
+ } else {
228
+ const skipped = files.filter((f) => f.status === "skipped");
229
+ const byCode = /* @__PURE__ */ new Map();
230
+ for (const f of skipped)
231
+ if (f.skipped) byCode.set(f.skipped.code, (byCode.get(f.skipped.code) ?? 0) + 1);
232
+ const saved = splits.reduce((n, f) => n + (f.bytesSaved?.conservative ?? 0), 0);
233
+ log();
234
+ log(pc.bold("Summary"));
235
+ log(` files scanned: ${report.filesScanned} (${files.length} with "use client")`);
236
+ const written = Boolean(args.write || args.interactive);
237
+ const writeNote = written ? ` (${toWrite.length} written)` : args.check ? "" : " (dry run, nothing written)";
238
+ log(` files split: ${splits.length}${writeNote}`);
239
+ log(` files skipped: ${skipped.length}`);
240
+ for (const [code, count] of [...byCode].sort((a, b) => b[1] - a[1])) {
241
+ log(` ${String(count).padStart(3)} ${code.padEnd(30)} ${pc.dim(SKIP_TITLES[code])}`);
242
+ }
243
+ const unmeasured = splits.filter((f) => f.measureError).length;
244
+ const unmeasuredNote = unmeasured > 0 ? ` (${unmeasured} split${unmeasured === 1 ? "" : "s"} not measured)` : "";
245
+ if (report.files.some((f) => f.bytesSaved))
246
+ log(` estimated saving: ${formatBytes(saved)} gzip of client JavaScript${unmeasuredNote}`);
247
+ else if (unmeasured > 0) log(` estimated saving: unknown${unmeasuredNote}`);
248
+ if (!written && !args.check && splits.length > 0)
249
+ log(pc.dim("\nRun again with --write to apply."));
250
+ }
251
+ if (files.some((f) => f.skipped?.code === "internal-error")) process.exitCode = EXIT_ERROR;
252
+ else if (args.check && splits.length > 0) process.exitCode = EXIT_SPLITS_AVAILABLE;
253
+ else process.exitCode = EXIT_OK;
254
+ }
255
+ });
256
+ void runMain(main);
257
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, realpathSync, writeFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { defineCommand, runMain } from \"citty\";\nimport pc from \"picocolors\";\nimport { SKIP_TITLES } from \"./analysis/skip.js\";\nimport { ConfigError } from \"./config/config.js\";\nimport { formatBytes, runSplit } from \"./split.js\";\nimport type { FileResult, SkipCode } from \"./types.js\";\n\ndeclare const __VERSION__: string;\n\nconst EXIT_OK = 0;\nconst EXIT_ERROR = 1;\nconst EXIT_SPLITS_AVAILABLE = 2;\n\nfunction colorDiff(diff: string): string {\n return diff\n .split(\"\\n\")\n .map((line) => {\n if (line.startsWith(\"+++\") || line.startsWith(\"---\") || line.startsWith(\"===\"))\n return pc.bold(line);\n if (line.startsWith(\"+\")) return pc.green(line);\n if (line.startsWith(\"-\")) return pc.red(line);\n if (line.startsWith(\"@@\")) return pc.cyan(line);\n return line;\n })\n .join(\"\\n\");\n}\n\nfunction describeSplit(file: FileResult): string {\n const islands = file.islands.map(\n (i) => `${i.name} <${i.rootTag}>${i.props.length ? ` (props: ${i.props.join(\", \")})` : \"\"}`,\n );\n const saved = file.bytesSaved\n ? ` · ~${formatBytes(file.bytesSaved.conservative)} gzip saved (estimated)`\n : file.measureError\n ? ` · ${pc.yellow(\"saving not measured\")} (${file.measureError})`\n : \"\";\n return `${pc.green(\"✔ split\")} ${pc.bold(file.file)} · ${file.islands.length} island${file.islands.length === 1 ? \"\" : \"s\"} · ${Math.round(file.staticRatio * 100)}% static${saved}\\n ${islands.join(\"\\n \")}`;\n}\n\nfunction describeSkip(file: FileResult, verbose: boolean): string {\n const s = file.skipped;\n if (!s) return \"\";\n const loc = s.location ? `:${s.location.line}:${s.location.column}` : \"\";\n let message = s.message;\n if (s.code === \"internal-error\" && !verbose) message = message.split(\"\\n\")[0] ?? message;\n const color = s.code === \"internal-error\" || s.code === \"self-check-failed\" ? pc.red : pc.yellow;\n return `${color(\"– skipped\")} ${file.file}${loc} ${pc.dim(`[${s.code}]`)} ${message}`;\n}\n\n/**\n * Target files that git can't restore: modified, staged, untracked or ignored. Undefined when git\n * can't tell us (not a repository).\n */\nfunction dirtyFiles(cwd: string, files: string[]): string[] | undefined {\n if (files.length === 0) return [];\n try {\n const out = execFileSync(\n \"git\",\n [\"status\", \"--porcelain\", \"--ignored\", \"--untracked-files=all\", \"--\", ...files],\n {\n cwd,\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n },\n );\n return out\n .split(\"\\n\")\n .filter(Boolean)\n .map(\n (l) =>\n `${l.slice(3)}${l.startsWith(\"!!\") ? \" (ignored by git)\" : l.startsWith(\"??\") ? \" (untracked)\" : \"\"}`,\n );\n } catch {\n return undefined;\n }\n}\n\nconst main = defineCommand({\n meta: {\n name: \"auto-server-split\",\n version: typeof __VERSION__ === \"string\" ? __VERSION__ : \"0.0.0-dev\",\n description:\n 'Split partly-interactive \"use client\" components into a Server Component plus small client islands.',\n },\n args: {\n paths: {\n type: \"positional\",\n required: false,\n description: \"Files or directories (default: the config `include` globs)\",\n },\n \"dry-run\": { type: \"boolean\", description: \"Show the diff without writing anything (default)\" },\n write: { type: \"boolean\", description: \"Apply the changes\" },\n check: {\n type: \"boolean\",\n description: \"CI mode: exit 2 if any split is available, change nothing\",\n },\n interactive: {\n type: \"boolean\",\n description: \"Review each proposed split ([y]es/[n]o/[r]ename/[d]iff/[q]uit)\",\n },\n json: { type: \"boolean\", description: \"Print a machine-readable report\" },\n config: {\n type: \"string\",\n description: \"Config file (default: auto-split.config.* in the project root)\",\n },\n root: { type: \"string\", description: \"Project root (where tsconfig.json lives; default: cwd)\" },\n \"allow-dirty\": {\n type: \"boolean\",\n description: \"Allow --write on files with uncommitted git changes\",\n },\n verbose: { type: \"boolean\", description: \"Show stack traces for internal errors\" },\n measure: {\n type: \"boolean\",\n default: true,\n description: \"Measure bundle sizes with esbuild (--no-measure to skip)\",\n },\n },\n async run({ args }) {\n const cwd = process.cwd();\n const rootArg = path.resolve(cwd, args.root ?? \".\");\n if (!existsSync(rootArg)) {\n console.error(pc.red(`auto-server-split: ${args.root ?? \".\"} does not exist`));\n process.exitCode = EXIT_ERROR;\n return;\n }\n const root = realpathSync(rootArg);\n const positional = (args._ as string[]).filter(Boolean);\n for (const p of positional) {\n if (!existsSync(path.resolve(cwd, p))) {\n console.error(pc.red(`auto-server-split: ${p} does not exist`));\n process.exitCode = EXIT_ERROR;\n return;\n }\n }\n const paths = positional.map(\n (p) => path.relative(root, realpathSync(path.resolve(cwd, p))) || \".\",\n );\n // --interactive writes the splits you accept, so it can't be a dry run.\n if (args.interactive && (args[\"dry-run\"] || args.json || args.check)) {\n console.error(\n pc.red(\n \"auto-server-split: --interactive can't be combined with --dry-run, --json or --check\",\n ),\n );\n process.exitCode = EXIT_ERROR;\n return;\n }\n if (args.interactive && !process.stdin.isTTY) {\n console.error(pc.red(\"auto-server-split: --interactive needs a terminal\"));\n process.exitCode = EXIT_ERROR;\n return;\n }\n const modes = [args[\"dry-run\"], args.write, args.check].filter(Boolean).length;\n if (modes > 1) {\n console.error(pc.red(\"auto-server-split: use only one of --dry-run, --write, --check\"));\n process.exitCode = EXIT_ERROR;\n return;\n }\n const verbose = Boolean(args.verbose);\n const json = Boolean(args.json);\n const log = (line = \"\") => {\n if (!json) console.log(line);\n };\n\n let report;\n try {\n report = await runSplit({\n root,\n ...(paths.length > 0 ? { paths } : {}),\n write: false,\n ...(args.config ? { configFile: args.config } : {}),\n measure: args.measure !== false,\n onFile: (file) => {\n if (json || args.interactive) return;\n if (file.status === \"split\") {\n log(describeSplit(file));\n if (!args.check) log(colorDiff(file.diff));\n } else if (file.skipped) {\n log(describeSkip(file, verbose));\n }\n },\n });\n } catch (error) {\n if (error instanceof ConfigError)\n console.error(pc.red(`auto-server-split: invalid config\\n${error.message}`));\n else console.error(pc.red(`auto-server-split: ${(error as Error).stack ?? String(error)}`));\n process.exitCode = EXIT_ERROR;\n return;\n }\n\n let files = report.files;\n let toWrite = args.write ? files.filter((f) => f.status === \"split\") : [];\n if (args.interactive) {\n const { reviewInteractively } = await import(\"./interactive.js\");\n toWrite = await reviewInteractively(files, report.rerun, {\n input: process.stdin,\n output: process.stdout,\n colorDiff,\n describe: describeSplit,\n });\n const acceptedByFile = new Map(toWrite.map((f) => [f.file, f]));\n files = files.map((f) => acceptedByFile.get(f.file) ?? f);\n }\n\n const splits = files.filter((f) => f.status === \"split\");\n if (toWrite.length > 0) {\n const touched = toWrite.flatMap((f) => Object.keys(f.outputs));\n if (!args[\"allow-dirty\"]) {\n const dirty = dirtyFiles(\n root,\n touched.filter((t) => existsSync(path.join(root, t))),\n );\n if (dirty === undefined) {\n console.error(\n pc.red(\n \"auto-server-split: can't read git status (not a git repository?). Commit your work or pass --allow-dirty.\",\n ),\n );\n process.exitCode = EXIT_ERROR;\n return;\n }\n if (dirty.length > 0) {\n console.error(\n pc.red(\n `auto-server-split: refusing to write; these files have uncommitted changes (or aren't tracked by git):\\n ${dirty.join(\"\\n \")}\\nCommit them or pass --allow-dirty.`,\n ),\n );\n process.exitCode = EXIT_ERROR;\n return;\n }\n }\n for (const file of toWrite) {\n for (const [rel, content] of Object.entries(file.outputs)) {\n const abs = path.join(root, rel);\n mkdirSync(path.dirname(abs), { recursive: true });\n writeFileSync(abs, content);\n }\n }\n }\n\n if (json) {\n console.log(JSON.stringify({ files }, null, 2));\n } else {\n const skipped = files.filter((f) => f.status === \"skipped\");\n const byCode = new Map<SkipCode, number>();\n for (const f of skipped)\n if (f.skipped) byCode.set(f.skipped.code, (byCode.get(f.skipped.code) ?? 0) + 1);\n const saved = splits.reduce((n, f) => n + (f.bytesSaved?.conservative ?? 0), 0);\n log();\n log(pc.bold(\"Summary\"));\n log(` files scanned: ${report.filesScanned} (${files.length} with \"use client\")`);\n const written = Boolean(args.write || args.interactive);\n const writeNote = written\n ? ` (${toWrite.length} written)`\n : args.check\n ? \"\"\n : \" (dry run, nothing written)\";\n log(` files split: ${splits.length}${writeNote}`);\n log(` files skipped: ${skipped.length}`);\n for (const [code, count] of [...byCode].sort((a, b) => b[1] - a[1])) {\n log(` ${String(count).padStart(3)} ${code.padEnd(30)} ${pc.dim(SKIP_TITLES[code])}`);\n }\n const unmeasured = splits.filter((f) => f.measureError).length;\n const unmeasuredNote =\n unmeasured > 0 ? ` (${unmeasured} split${unmeasured === 1 ? \"\" : \"s\"} not measured)` : \"\";\n if (report.files.some((f) => f.bytesSaved))\n log(` estimated saving: ${formatBytes(saved)} gzip of client JavaScript${unmeasuredNote}`);\n else if (unmeasured > 0) log(` estimated saving: unknown${unmeasuredNote}`);\n if (!written && !args.check && splits.length > 0)\n log(pc.dim(\"\\nRun again with --write to apply.\"));\n }\n\n if (files.some((f) => f.skipped?.code === \"internal-error\")) process.exitCode = EXIT_ERROR;\n else if (args.check && splits.length > 0) process.exitCode = EXIT_SPLITS_AVAILABLE;\n else process.exitCode = EXIT_OK;\n },\n});\n\nvoid runMain(main);\n"],"mappings":"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","names":[]}
@@ -0,0 +1,17 @@
1
+ import * as eslint from 'eslint';
2
+ import { Linter } from 'eslint';
3
+
4
+ declare const plugin: {
5
+ meta: {
6
+ name: string;
7
+ version: string;
8
+ };
9
+ rules: {
10
+ "prefer-server-split": eslint.Rule.RuleModule;
11
+ };
12
+ configs: {
13
+ recommended: Linter.Config;
14
+ };
15
+ };
16
+
17
+ export { plugin as default };