@proj-airi/cap-vite 0.9.0-alpha.9 → 0.9.0-beta.2

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
@@ -5,25 +5,33 @@ CLI for [Capacitor](https://capacitorjs.com/) live-reload development using Vite
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
- pnpm cap-vite ios --target <DEVICE_ID_OR_SIMULATOR_NAME>
9
- pnpm cap-vite android --target <DEVICE_ID_OR_SIMULATOR_NAME>
10
- pnpm cap-vite ios --target <DEVICE_ID_OR_SIMULATOR_NAME> -- --scheme AIRI
11
- pnpm cap-vite android --target <DEVICE_ID_OR_SIMULATOR_NAME> -- --flavor release
12
- # Or
13
- CAPACITOR_DEVICE_ID=<DEVICE_ID_OR_SIMULATOR_NAME> pnpm cap-vite ios
14
- CAPACITOR_DEVICE_ID=<DEVICE_ID_OR_SIMULATOR_NAME> pnpm cap-vite android
8
+ cap-vite [vite args...] -- <ios|android> [cap run args...]
15
9
  ```
16
10
 
17
- - Arguments after `--` are forwarded to `cap run`, example: `pnpm cap-vite ios --target <DEVICE_ID_OR_SIMULATOR_NAME> -- --scheme AIRI` will run `cap run ios --target <DEVICE_ID_OR_SIMULATOR_NAME> --scheme AIRI`.
11
+ Examples:
12
+
13
+ ```bash
14
+ pnpm exec cap-vite -- ios --target <DEVICE_ID_OR_SIMULATOR_NAME>
15
+ pnpm exec cap-vite -- --host 0.0.0.0 --port 5173 -- android --target <DEVICE_ID_OR_SIMULATOR_NAME> --flavor release
16
+ CAPACITOR_DEVICE_ID_IOS=<DEVICE_ID_OR_SIMULATOR_NAME> pnpm exec cap-vite -- ios
17
+ pnpm -F @proj-airi/stage-pocket run dev:ios -- --target <DEVICE_ID_OR_SIMULATOR_NAME>
18
+ ```
19
+
20
+ - Arguments before `--` are forwarded to `vite`.
21
+ - Arguments after `--` are forwarded to `cap run`.
22
+ - If the platform-specific env is set (`CAPACITOR_DEVICE_ID_IOS` or `CAPACITOR_DEVICE_ID_ANDROID`) and `cap run` args do not contain `--target`, `cap-vite` injects `--target` with that value automatically.
23
+ - `cap-vite` always launches the Vite dev server. Do not pass `vite dev` or `vite serve` as extra args.
24
+ - After the dev server starts, press `R` in the terminal to re-run `cap run` without restarting Vite.
18
25
 
19
26
  You can see the list of available devices and simulators by running `pnpm exec cap run ios --list` or `pnpm exec cap run android --list`.
20
27
 
21
28
  ## Capacitor Configuration
22
29
 
23
- You need to set `server.url` in `capacitor.config.ts` to the env variable `CAPACITOR_DEV_SERVER_URL`.
30
+ You need to set `server.url` in `capacitor.config.ts` to the env variable `CAPACITOR_DEV_SERVER_URL`, then the cli will handle rest for you.
24
31
 
25
32
  ```ts
26
33
  const serverURL = env.CAPACITOR_DEV_SERVER_URL
34
+ const isCleartext = serverURL?.startsWith('http://') ?? false
27
35
 
28
36
  const config: CapacitorConfig = {
29
37
  appId: 'com.example.app',
@@ -32,7 +40,7 @@ const config: CapacitorConfig = {
32
40
  server: serverURL
33
41
  ? {
34
42
  url: serverURL,
35
- cleartext: false,
43
+ cleartext: isCleartext,
36
44
  }
37
45
  : undefined,
38
46
  }
@@ -40,8 +48,16 @@ const config: CapacitorConfig = {
40
48
  export default config
41
49
  ```
42
50
 
43
- ## What It Does
51
+ ## Why we need this?
52
+
53
+ - No need to care what `server.url` should be, it will be automatically set to the correct value.
54
+ - Rerun native app when native code changes, you won't forget to start it.
55
+ - Rerun `cap run` on demand from the same terminal when you need a clean native relaunch.
56
+ - No need to open two terminals to run the project, you can run it with one command.
57
+
58
+ ## Architecture Notes
44
59
 
45
- - Starts the project's own Vite config through the Vite API.
46
- - Executes the local `cap` binary via `tinyexec`.
47
- - Watches native files under `ios/` or `android/` and re-runs `cap run` after a small debounce.
60
+ - Vite arguments are left to the real Vite CLI instead of being reimplemented inside `cap-vite`.
61
+ - `cap-vite` injects a wrapper config so it can append its own Vite plugin without editing the user's existing `vite.config.*`.
62
+ - The injected plugin reads `server.resolvedUrls`, starts `cap run`, and restarts it when files under the native platform directory change or when you press `R` in the terminal.
63
+ - `cap-vite` only splits the two argument groups and passes the `cap run` arguments into the injected plugin through environment variables.
package/dist/bin/run.mjs CHANGED
@@ -1,51 +1,55 @@
1
1
  #!/usr/bin/env node
2
2
  import { runCapVite } from "../index.mjs";
3
3
  import process from "node:process";
4
- import { cac } from "cac";
5
-
6
4
  //#region src/cli.ts
7
- const usage = "cap-vite <ios|android> [--target <DEVICE_ID_OR_SIMULATOR_NAME>] [-- <cap args...>]";
8
- function createCapViteCli() {
9
- const cli = cac("cap-vite");
10
- cli.help();
11
- cli.command("<platform>", "Run Capacitor with a Vite dev server").usage(usage).option("--target <target>", "Set the Capacitor device target").example("cap-vite ios --target \"iPhone 16 Pro\" -- --scheme AIRI").example("CAPACITOR_DEVICE_ID=emulator-5554 cap-vite android -- --flavor release");
12
- return cli;
5
+ const usage = "cap-vite [vite args...] -- <ios|android> [cap run args...]";
6
+ const helpText = [
7
+ "Run a Vite dev server and forward a second argument group to `cap run`.",
8
+ "",
9
+ "Usage:",
10
+ ` ${usage}`,
11
+ "",
12
+ "Examples:",
13
+ " cap-vite -- ios --target \"iPhone 16 Pro\"",
14
+ " cap-vite --host 0.0.0.0 --port 5173 -- android --target emulator-5554 --flavor release",
15
+ "",
16
+ "Notes:",
17
+ " Arguments before `--` are forwarded to Vite.",
18
+ " Arguments after `--` are forwarded to `cap run`.",
19
+ " After the dev server starts, press `R` to re-run `cap run`."
20
+ ].join("\n");
21
+ function getCapViteCliHelpText() {
22
+ return helpText;
13
23
  }
14
- function parseCapViteCliArgs(argv, env = process.env) {
15
- const cli = createCapViteCli();
16
- const parsed = cli.parse([
17
- "node",
18
- "cap-vite",
19
- ...argv
20
- ], { run: false });
21
- if (cli.options.help) return null;
22
- cli.matchedCommand?.checkUnknownOptions();
23
- cli.matchedCommand?.checkOptionValue();
24
- cli.matchedCommand?.checkRequiredArgs();
25
- if (parsed.args.length > 1) throw new Error(usage);
26
- const platform = parsed.args[0];
24
+ function parseCapViteCliArgs(argv) {
25
+ if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h")) return null;
26
+ const separatorIndex = argv.indexOf("--");
27
+ if (separatorIndex === -1) throw new Error(usage);
28
+ const capArgs = argv.slice(separatorIndex + 1);
29
+ if (capArgs.length === 0) throw new Error(usage);
30
+ const platform = capArgs[0];
27
31
  if (platform !== "android" && platform !== "ios") throw new Error(usage);
28
- const target = typeof parsed.options.target === "string" ? parsed.options.target : env.CAPACITOR_DEVICE_ID;
29
- if (!target) throw new Error(usage);
30
32
  return {
31
- capArgs: Array.isArray(parsed.options["--"]) ? parsed.options["--"] : [],
32
- platform,
33
- target
33
+ capArgs,
34
+ viteArgs: argv.slice(0, separatorIndex)
34
35
  };
35
36
  }
36
-
37
37
  //#endregion
38
38
  //#region src/bin/run.ts
39
39
  async function main() {
40
40
  const parsed = parseCapViteCliArgs(process.argv.slice(2));
41
- if (!parsed) return;
42
- await runCapVite(parsed.platform, parsed.target, { capArgs: parsed.capArgs });
41
+ if (!parsed) {
42
+ process.stdout.write(`${getCapViteCliHelpText()}\n`);
43
+ return;
44
+ }
45
+ const result = await runCapVite(parsed.viteArgs, parsed.capArgs);
46
+ if (typeof result.exitCode === "number") process.exitCode = result.exitCode;
43
47
  }
44
48
  main().catch((error) => {
45
49
  process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
46
50
  process.exit(1);
47
51
  });
48
-
49
52
  //#endregion
50
- export { };
53
+ export {};
54
+
51
55
  //# sourceMappingURL=run.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.mjs","names":[],"sources":["../../src/cli.ts","../../src/bin/run.ts"],"sourcesContent":["import type { CapacitorPlatform } from '.'\n\nimport process from 'node:process'\n\nimport { cac } from 'cac'\n\nexport interface ParsedCapViteCliArgs {\n capArgs: string[]\n platform: CapacitorPlatform\n target: string\n}\n\nconst usage = 'cap-vite <ios|android> [--target <DEVICE_ID_OR_SIMULATOR_NAME>] [-- <cap args...>]'\n\nfunction createCapViteCli() {\n const cli = cac('cap-vite')\n\n cli.help()\n cli\n .command('<platform>', 'Run Capacitor with a Vite dev server')\n .usage(usage)\n .option('--target <target>', 'Set the Capacitor device target')\n .example('cap-vite ios --target \"iPhone 16 Pro\" -- --scheme AIRI')\n .example('CAPACITOR_DEVICE_ID=emulator-5554 cap-vite android -- --flavor release')\n\n return cli\n}\n\nexport function parseCapViteCliArgs(\n argv: string[],\n env: NodeJS.ProcessEnv = process.env,\n): ParsedCapViteCliArgs | null {\n const cli = createCapViteCli()\n const parsed = cli.parse(['node', 'cap-vite', ...argv], { run: false })\n\n if (cli.options.help) {\n return null\n }\n\n cli.matchedCommand?.checkUnknownOptions()\n cli.matchedCommand?.checkOptionValue()\n cli.matchedCommand?.checkRequiredArgs()\n\n if (parsed.args.length > 1) {\n throw new Error(usage)\n }\n\n const platform = parsed.args[0]\n if (platform !== 'android' && platform !== 'ios') {\n throw new Error(usage)\n }\n\n const target = typeof parsed.options.target === 'string' ? parsed.options.target : env.CAPACITOR_DEVICE_ID\n if (!target) {\n throw new Error(usage)\n }\n\n return {\n capArgs: Array.isArray(parsed.options['--']) ? parsed.options['--'] : [],\n platform,\n target,\n }\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process'\n\nimport { runCapVite } from '..'\nimport { parseCapViteCliArgs } from '../cli'\n\nasync function main() {\n const parsed = parseCapViteCliArgs(process.argv.slice(2))\n if (!parsed) {\n return\n }\n\n await runCapVite(parsed.platform, parsed.target, { capArgs: parsed.capArgs })\n}\n\nvoid main().catch((error) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n})\n"],"mappings":";;;;;;AAYA,MAAM,QAAQ;AAEd,SAAS,mBAAmB;CAC1B,MAAM,MAAM,IAAI,WAAW;AAE3B,KAAI,MAAM;AACV,KACG,QAAQ,cAAc,uCAAuC,CAC7D,MAAM,MAAM,CACZ,OAAO,qBAAqB,kCAAkC,CAC9D,QAAQ,2DAAyD,CACjE,QAAQ,yEAAyE;AAEpF,QAAO;;AAGT,SAAgB,oBACd,MACA,MAAyB,QAAQ,KACJ;CAC7B,MAAM,MAAM,kBAAkB;CAC9B,MAAM,SAAS,IAAI,MAAM;EAAC;EAAQ;EAAY,GAAG;EAAK,EAAE,EAAE,KAAK,OAAO,CAAC;AAEvE,KAAI,IAAI,QAAQ,KACd,QAAO;AAGT,KAAI,gBAAgB,qBAAqB;AACzC,KAAI,gBAAgB,kBAAkB;AACtC,KAAI,gBAAgB,mBAAmB;AAEvC,KAAI,OAAO,KAAK,SAAS,EACvB,OAAM,IAAI,MAAM,MAAM;CAGxB,MAAM,WAAW,OAAO,KAAK;AAC7B,KAAI,aAAa,aAAa,aAAa,MACzC,OAAM,IAAI,MAAM,MAAM;CAGxB,MAAM,SAAS,OAAO,OAAO,QAAQ,WAAW,WAAW,OAAO,QAAQ,SAAS,IAAI;AACvF,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,MAAM;AAGxB,QAAO;EACL,SAAS,MAAM,QAAQ,OAAO,QAAQ,MAAM,GAAG,OAAO,QAAQ,QAAQ,EAAE;EACxE;EACA;EACD;;;;;ACtDH,eAAe,OAAO;CACpB,MAAM,SAAS,oBAAoB,QAAQ,KAAK,MAAM,EAAE,CAAC;AACzD,KAAI,CAAC,OACH;AAGF,OAAM,WAAW,OAAO,UAAU,OAAO,QAAQ,EAAE,SAAS,OAAO,SAAS,CAAC;;AAG1E,MAAM,CAAC,OAAO,UAAU;AAC3B,SAAQ,OAAO,MAAM,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC,IAAI;AACnF,SAAQ,KAAK,EAAE;EACf"}
1
+ {"version":3,"file":"run.mjs","names":[],"sources":["../../src/cli.ts","../../src/bin/run.ts"],"sourcesContent":["const usage = 'cap-vite [vite args...] -- <ios|android> [cap run args...]'\n\nconst helpText = [\n 'Run a Vite dev server and forward a second argument group to `cap run`.',\n '',\n 'Usage:',\n ` ${usage}`,\n '',\n 'Examples:',\n ' cap-vite -- ios --target \"iPhone 16 Pro\"',\n ' cap-vite --host 0.0.0.0 --port 5173 -- android --target emulator-5554 --flavor release',\n '',\n 'Notes:',\n ' Arguments before `--` are forwarded to Vite.',\n ' Arguments after `--` are forwarded to `cap run`.',\n ' After the dev server starts, press `R` to re-run `cap run`.',\n].join('\\n')\n\nexport interface ParsedCapViteCliArgs {\n capArgs: string[]\n viteArgs: string[]\n}\n\nexport function getCapViteCliHelpText(): string {\n return helpText\n}\n\nexport function getCapViteCliUsage(): string {\n return usage\n}\n\n// TODO: CLI and `cap run` argument handling are hand-rolled (see also `resolveCapRunArgs` /\n// `hasCapacitorTargetArg` in native.ts). If parsing rules keep growing, adopt a dedicated argv\n// library (cac is already a dependency—consider subcommands or a small wrapper) so flags like\n// `--target` / `--target=`, env-based defaults, and validation stay in one maintainable layer.\n\nexport function parseCapViteCliArgs(argv: string[]): ParsedCapViteCliArgs | null {\n if (argv.length === 1 && (argv[0] === '--help' || argv[0] === '-h')) {\n return null\n }\n\n const separatorIndex = argv.indexOf('--')\n if (separatorIndex === -1) {\n throw new Error(usage)\n }\n\n const capArgs = argv.slice(separatorIndex + 1)\n if (capArgs.length === 0) {\n throw new Error(usage)\n }\n\n const platform = capArgs[0]\n if (platform !== 'android' && platform !== 'ios') {\n throw new Error(usage)\n }\n\n return {\n capArgs,\n viteArgs: argv.slice(0, separatorIndex),\n }\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process'\n\nimport { runCapVite } from '..'\nimport { getCapViteCliHelpText, parseCapViteCliArgs } from '../cli'\n\nasync function main() {\n const parsed = parseCapViteCliArgs(process.argv.slice(2))\n if (!parsed) {\n process.stdout.write(`${getCapViteCliHelpText()}\\n`)\n return\n }\n\n const result = await runCapVite(parsed.viteArgs, parsed.capArgs)\n if (typeof result.exitCode === 'number') {\n process.exitCode = result.exitCode\n }\n}\n\nvoid main().catch((error) => {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n})\n"],"mappings":";;;;AAAA,MAAM,QAAQ;AAEd,MAAM,WAAW;CACf;CACA;CACA;CACA,KAAK;CACL;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAOZ,SAAgB,wBAAgC;AAC9C,QAAO;;AAYT,SAAgB,oBAAoB,MAA6C;AAC/E,KAAI,KAAK,WAAW,MAAM,KAAK,OAAO,YAAY,KAAK,OAAO,MAC5D,QAAO;CAGT,MAAM,iBAAiB,KAAK,QAAQ,KAAK;AACzC,KAAI,mBAAmB,GACrB,OAAM,IAAI,MAAM,MAAM;CAGxB,MAAM,UAAU,KAAK,MAAM,iBAAiB,EAAE;AAC9C,KAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MAAM,MAAM;CAGxB,MAAM,WAAW,QAAQ;AACzB,KAAI,aAAa,aAAa,aAAa,MACzC,OAAM,IAAI,MAAM,MAAM;AAGxB,QAAO;EACL;EACA,UAAU,KAAK,MAAM,GAAG,eAAe;EACxC;;;;ACpDH,eAAe,OAAO;CACpB,MAAM,SAAS,oBAAoB,QAAQ,KAAK,MAAM,EAAE,CAAC;AACzD,KAAI,CAAC,QAAQ;AACX,UAAQ,OAAO,MAAM,GAAG,uBAAuB,CAAC,IAAI;AACpD;;CAGF,MAAM,SAAS,MAAM,WAAW,OAAO,UAAU,OAAO,QAAQ;AAChE,KAAI,OAAO,OAAO,aAAa,SAC7B,SAAQ,WAAW,OAAO;;AAIzB,MAAM,CAAC,OAAO,UAAU;AAC3B,SAAQ,OAAO,MAAM,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC,IAAI;AACnF,SAAQ,KAAK,EAAE;EACf"}
package/dist/index.d.mts CHANGED
@@ -1,11 +1,20 @@
1
- //#region src/index.d.ts
1
+ import { Output } from "tinyexec";
2
+ //#region src/native.d.ts
2
3
  type CapacitorPlatform = 'android' | 'ios';
4
+ //#endregion
5
+ //#region src/index.d.ts
3
6
  interface RunCapViteOptions {
4
- capArgs?: string[];
5
7
  cwd?: string;
6
- debounceMs?: number;
7
8
  }
8
- declare function runCapVite(platform: CapacitorPlatform, target: string, options?: RunCapViteOptions): Promise<void>;
9
+ interface PreparedViteLaunch {
10
+ baseConfigFile?: string;
11
+ configLoader?: 'bundle' | 'native' | 'runner';
12
+ projectRoot: string;
13
+ viteArgs: string[];
14
+ wrapperConfigFile: string;
15
+ }
16
+ declare function prepareCapViteLaunch(viteArgs: string[], cwd?: string): PreparedViteLaunch;
17
+ declare function runCapVite(viteArgs: string[], capArgs: string[], options?: RunCapViteOptions): Promise<Output>;
9
18
  //#endregion
10
- export { CapacitorPlatform, RunCapViteOptions, runCapVite };
19
+ export { type CapacitorPlatform, RunCapViteOptions, prepareCapViteLaunch, runCapVite };
11
20
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,156 +1,109 @@
1
+ import { t as parseCapacitorPlatform } from "./native-hZ5d7ukC.mjs";
1
2
  import process from "node:process";
2
- import { basename, extname, relative, resolve, sep } from "node:path";
3
+ import { extname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
3
5
  import { x } from "tinyexec";
4
- import { createServer } from "vite";
5
-
6
6
  //#region src/index.ts
7
- const nativeExtensionsByPlatform = {
8
- ios: new Set([
9
- ".entitlements",
10
- ".h",
11
- ".hpp",
12
- ".m",
13
- ".mm",
14
- ".pbxproj",
15
- ".plist",
16
- ".storyboard",
17
- ".strings",
18
- ".swift",
19
- ".xcodeproj",
20
- ".xcconfig",
21
- ".xcscheme",
22
- ".xib"
23
- ]),
24
- android: new Set([
25
- ".gradle",
26
- ".java",
27
- ".json",
28
- ".kts",
29
- ".kt",
30
- ".properties",
31
- ".xml"
32
- ])
33
- };
34
- const nativeNamesByPlatform = {
35
- ios: new Set([
36
- "Podfile",
37
- "Podfile.lock",
38
- "project.pbxproj"
39
- ]),
40
- android: new Set([
41
- "AndroidManifest.xml",
42
- "build.gradle",
43
- "build.gradle.kts",
44
- "gradle.properties",
45
- "settings.gradle",
46
- "settings.gradle.kts"
47
- ])
48
- };
49
- const ignoredNames = new Set(["capacitor.config.json"]);
50
- const ignoredPathSegments = new Set([
51
- ".gradle",
52
- "DerivedData",
53
- "Pods",
54
- "build",
55
- "xcuserdata"
56
- ]);
57
- const ignoredPathPrefixesByPlatform = {
58
- ios: [["App", "CapApp-SPM"]],
59
- android: []
60
- };
61
- function pickServerUrl(server) {
62
- const url = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0];
63
- if (!url) throw new Error("Vite did not expose a reachable dev server URL.");
64
- return new URL(url);
7
+ function resolveWrapperConfigFile() {
8
+ const wrapperExtension = extname(fileURLToPath(import.meta.url)) === ".ts" ? ".ts" : ".mjs";
9
+ return fileURLToPath(new URL(`./vite-wrapper-config${wrapperExtension}`, import.meta.url));
10
+ }
11
+ function parseViteConfigLoader(value) {
12
+ if (value === "bundle" || value === "native" || value === "runner") return value;
65
13
  }
66
- function shouldRestartForNativeChange(file, platform, cwd) {
67
- const absoluteFile = resolve(cwd, file);
68
- const platformRoot = resolve(cwd, platform);
69
- if (!absoluteFile.startsWith(`${platformRoot}${sep}`) && absoluteFile !== platformRoot) return false;
70
- const fileName = basename(absoluteFile);
71
- if (ignoredNames.has(fileName)) return false;
72
- if (absoluteFile.split(sep).some((segment) => ignoredPathSegments.has(segment))) return false;
73
- const relativeSegments = relative(platformRoot, absoluteFile).split(sep).filter(Boolean);
74
- if (ignoredPathPrefixesByPlatform[platform].some((prefix) => prefix.every((segment, index) => relativeSegments[index] === segment))) return false;
75
- if (nativeNamesByPlatform[platform].has(fileName)) return true;
76
- return nativeExtensionsByPlatform[platform].has(extname(fileName).toLowerCase());
14
+ function resolveConfigPath(cwd, value) {
15
+ return resolve(cwd, value);
77
16
  }
78
- async function stopCapProcess(current) {
79
- if (!current) return;
80
- current.kill("SIGINT");
81
- try {
82
- await current;
83
- } catch {}
17
+ function readRequiredOptionValue(viteArgs, index, optionName) {
18
+ const value = viteArgs[index + 1];
19
+ if (!value) throw new Error(`Missing value for \`${optionName}\`.`);
20
+ return value;
84
21
  }
85
- function startCapProcess(cwd, platform, target, url, capArgs) {
86
- console.info("\n----------------------\n");
87
- console.info("Running cap run", platform, "--target", target, ...capArgs);
88
- return x("cap", [
89
- "run",
90
- platform,
91
- "--target",
92
- target,
93
- ...capArgs
22
+ function parseConfigArg(viteArgs, index, cwd) {
23
+ const arg = viteArgs[index];
24
+ if (arg === "--config" || arg === "-c") return {
25
+ baseConfigFile: resolveConfigPath(cwd, readRequiredOptionValue(viteArgs, index, "--config")),
26
+ consumedArgs: 2,
27
+ forwardedArgs: []
28
+ };
29
+ if (arg.startsWith("--config=")) return {
30
+ baseConfigFile: resolveConfigPath(cwd, arg.slice(9)),
31
+ consumedArgs: 1,
32
+ forwardedArgs: []
33
+ };
34
+ return null;
35
+ }
36
+ function parseConfigLoaderArg(viteArgs, index) {
37
+ const arg = viteArgs[index];
38
+ if (arg === "--configLoader") {
39
+ const value = readRequiredOptionValue(viteArgs, index, "--configLoader");
40
+ return {
41
+ configLoader: parseViteConfigLoader(value),
42
+ consumedArgs: 2,
43
+ forwardedArgs: [arg, value]
44
+ };
45
+ }
46
+ if (arg.startsWith("--configLoader=")) return {
47
+ configLoader: parseViteConfigLoader(arg.slice(15)),
48
+ consumedArgs: 1,
49
+ forwardedArgs: [arg]
50
+ };
51
+ return null;
52
+ }
53
+ function parseViteArg(viteArgs, index, cwd) {
54
+ return parseConfigArg(viteArgs, index, cwd) ?? parseConfigLoaderArg(viteArgs, index) ?? {
55
+ consumedArgs: 1,
56
+ forwardedArgs: [viteArgs[index]]
57
+ };
58
+ }
59
+ function resolveProjectRoot(viteArgs, cwd) {
60
+ const firstArg = viteArgs[0];
61
+ return firstArg && !firstArg.startsWith("-") ? resolve(cwd, firstArg) : cwd;
62
+ }
63
+ function prepareCapViteLaunch(viteArgs, cwd = process.cwd()) {
64
+ const resolvedCwd = resolve(cwd);
65
+ const projectRoot = resolveProjectRoot(viteArgs, resolvedCwd);
66
+ let baseConfigFile;
67
+ let configLoader;
68
+ const forwardedViteArgs = [];
69
+ for (let index = 0; index < viteArgs.length;) {
70
+ const parsedArg = parseViteArg(viteArgs, index, resolvedCwd);
71
+ baseConfigFile = parsedArg.baseConfigFile ?? baseConfigFile;
72
+ configLoader = parsedArg.configLoader ?? configLoader;
73
+ forwardedViteArgs.push(...parsedArg.forwardedArgs);
74
+ index += parsedArg.consumedArgs;
75
+ }
76
+ return {
77
+ baseConfigFile,
78
+ configLoader,
79
+ projectRoot,
80
+ viteArgs: forwardedViteArgs,
81
+ wrapperConfigFile: resolveWrapperConfigFile()
82
+ };
83
+ }
84
+ async function runCapVite(viteArgs, capArgs, options = {}) {
85
+ if (!parseCapacitorPlatform(capArgs[0])) throw new Error("The first `cap run` argument must be `ios` or `android`.");
86
+ const cwd = resolve(options.cwd ?? process.cwd());
87
+ const prepared = prepareCapViteLaunch(viteArgs, cwd);
88
+ return await x("vite", [
89
+ "--config",
90
+ prepared.wrapperConfigFile,
91
+ ...prepared.viteArgs
94
92
  ], {
95
- persist: true,
96
93
  throwOnError: false,
97
94
  nodeOptions: {
98
95
  cwd,
99
- stdio: "inherit",
100
- env: { CAPACITOR_DEV_SERVER_URL: url.toString() }
96
+ env: {
97
+ CAP_VITE_BASE_CONFIG: prepared.baseConfigFile ?? "",
98
+ CAP_VITE_CAP_ARGS_JSON: JSON.stringify(capArgs),
99
+ CAP_VITE_CONFIG_LOADER: prepared.configLoader ?? "",
100
+ CAP_VITE_ROOT: prepared.projectRoot
101
+ },
102
+ stdio: "inherit"
101
103
  }
102
104
  });
103
105
  }
104
- async function runCapVite(platform, target, options = {}) {
105
- const capArgs = options.capArgs ?? [];
106
- const cwd = resolve(options.cwd ?? process.cwd());
107
- const debounceMs = options.debounceMs ?? 300;
108
- const server = await createServer({
109
- clearScreen: false,
110
- root: cwd
111
- });
112
- await server.listen();
113
- server.printUrls();
114
- const url = pickServerUrl(server);
115
- const logger = server.config.logger;
116
- let currentCapProcess = startCapProcess(cwd, platform, target, url, capArgs);
117
- let restartTimer;
118
- let shuttingDown = false;
119
- async function restartCapProcess(reason) {
120
- if (shuttingDown) return;
121
- logger.info(`[cap-vite] ${reason}. Re-running cap run ${platform}.`);
122
- const previous = currentCapProcess;
123
- currentCapProcess = void 0;
124
- await stopCapProcess(previous);
125
- currentCapProcess = startCapProcess(cwd, platform, target, url, capArgs);
126
- }
127
- const onWatcherEvent = (_event, file) => {
128
- if (!shouldRestartForNativeChange(file, platform, cwd)) return;
129
- clearTimeout(restartTimer);
130
- restartTimer = setTimeout(() => {
131
- restartCapProcess(`native file changed: ${resolve(cwd, file)}`);
132
- }, debounceMs);
133
- };
134
- const shutdown = async (exitCode) => {
135
- if (shuttingDown) return;
136
- shuttingDown = true;
137
- clearTimeout(restartTimer);
138
- server.watcher.off("all", onWatcherEvent);
139
- await server.watcher.unwatch(platform);
140
- await server.close();
141
- await stopCapProcess(currentCapProcess);
142
- process.exit(exitCode);
143
- };
144
- server.watcher.add(platform);
145
- server.watcher.on("all", onWatcherEvent);
146
- process.once("SIGINT", () => {
147
- shutdown(0);
148
- });
149
- process.once("SIGTERM", () => {
150
- shutdown(0);
151
- });
152
- }
153
-
154
106
  //#endregion
155
- export { runCapVite };
107
+ export { prepareCapViteLaunch, runCapVite };
108
+
156
109
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Result } from 'tinyexec'\nimport type { ViteDevServer } from 'vite'\n\nimport process from 'node:process'\n\nimport { basename, extname, relative, resolve, sep } from 'node:path'\n\nimport { x } from 'tinyexec'\nimport { createServer } from 'vite'\n\nexport type CapacitorPlatform = 'android' | 'ios'\n\nexport interface RunCapViteOptions {\n capArgs?: string[]\n cwd?: string\n debounceMs?: number\n}\n\nconst nativeExtensionsByPlatform: Record<CapacitorPlatform, Set<string>> = {\n ios: new Set([\n '.entitlements',\n '.h',\n '.hpp',\n '.m',\n '.mm',\n '.pbxproj',\n '.plist',\n '.storyboard',\n '.strings',\n '.swift',\n '.xcodeproj',\n '.xcconfig',\n '.xcscheme',\n '.xib',\n ]),\n android: new Set([\n '.gradle',\n '.java',\n '.json',\n '.kts',\n '.kt',\n '.properties',\n '.xml',\n ]),\n}\n\nconst nativeNamesByPlatform: Record<CapacitorPlatform, Set<string>> = {\n ios: new Set([\n 'Podfile',\n 'Podfile.lock',\n 'project.pbxproj',\n ]),\n android: new Set([\n 'AndroidManifest.xml',\n 'build.gradle',\n 'build.gradle.kts',\n 'gradle.properties',\n 'settings.gradle',\n 'settings.gradle.kts',\n ]),\n}\n\nconst ignoredNames = new Set([\n 'capacitor.config.json',\n])\n\nconst ignoredPathSegments = new Set([\n '.gradle',\n 'DerivedData',\n 'Pods',\n 'build',\n 'xcuserdata',\n])\n\nconst ignoredPathPrefixesByPlatform: Record<CapacitorPlatform, string[][]> = {\n ios: [\n ['App', 'CapApp-SPM'],\n ],\n android: [],\n}\n\nfunction pickServerUrl(server: ViteDevServer): URL {\n const url = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0]\n\n if (!url) {\n throw new Error('Vite did not expose a reachable dev server URL.')\n }\n\n const resolved = new URL(url)\n\n return resolved\n}\n\nfunction shouldRestartForNativeChange(file: string, platform: CapacitorPlatform, cwd: string): boolean {\n const absoluteFile = resolve(cwd, file)\n const platformRoot = resolve(cwd, platform)\n\n if (!absoluteFile.startsWith(`${platformRoot}${sep}`) && absoluteFile !== platformRoot) {\n return false\n }\n\n const fileName = basename(absoluteFile)\n\n if (ignoredNames.has(fileName)) {\n return false\n }\n\n const segments = absoluteFile.split(sep)\n if (segments.some(segment => ignoredPathSegments.has(segment))) {\n return false\n }\n\n const relativeFile = relative(platformRoot, absoluteFile)\n const relativeSegments = relativeFile.split(sep).filter(Boolean)\n\n if (ignoredPathPrefixesByPlatform[platform].some(prefix =>\n prefix.every((segment, index) => relativeSegments[index] === segment),\n )) {\n // NOTICE: Capacitor regenerates ios/App/CapApp-SPM/Package.swift during `cap run`.\n // Treating that generated tree as a native source change causes an infinite restart loop.\n return false\n }\n\n if (nativeNamesByPlatform[platform].has(fileName)) {\n return true\n }\n\n return nativeExtensionsByPlatform[platform].has(extname(fileName).toLowerCase())\n}\n\nasync function stopCapProcess(current: Result | undefined) {\n if (!current) {\n return\n }\n\n current.kill('SIGINT')\n\n try {\n await current\n }\n catch {\n // tinyexec rejects on interrupted exits when the child was stopped for a restart.\n }\n}\n\nfunction startCapProcess(cwd: string, platform: CapacitorPlatform, target: string, url: URL, capArgs: string[]) {\n console.info('\\n----------------------\\n')\n console.info('Running cap run', platform, '--target', target, ...capArgs)\n return x('cap', ['run', platform, '--target', target, ...capArgs], { persist: true, throwOnError: false, nodeOptions: { cwd, stdio: 'inherit', env: { CAPACITOR_DEV_SERVER_URL: url.toString() } } })\n}\n\nexport async function runCapVite(\n platform: CapacitorPlatform,\n target: string,\n options: RunCapViteOptions = {},\n): Promise<void> {\n const capArgs = options.capArgs ?? []\n const cwd = resolve(options.cwd ?? process.cwd())\n const debounceMs = options.debounceMs ?? 300\n const server = await createServer({\n clearScreen: false,\n root: cwd,\n })\n\n await server.listen()\n server.printUrls()\n\n const url = pickServerUrl(server)\n const logger = server.config.logger\n\n let currentCapProcess: Result | undefined = startCapProcess(cwd, platform, target, url, capArgs)\n let restartTimer: NodeJS.Timeout | undefined\n let shuttingDown = false\n\n async function restartCapProcess(reason: string) {\n if (shuttingDown) {\n return\n }\n\n logger.info(`[cap-vite] ${reason}. Re-running cap run ${platform}.`)\n const previous = currentCapProcess\n currentCapProcess = undefined\n await stopCapProcess(previous)\n currentCapProcess = startCapProcess(cwd, platform, target, url, capArgs)\n }\n\n const onWatcherEvent = (_event: string, file: string) => {\n if (!shouldRestartForNativeChange(file, platform, cwd)) {\n return\n }\n\n clearTimeout(restartTimer)\n restartTimer = setTimeout(() => {\n void restartCapProcess(`native file changed: ${resolve(cwd, file)}`)\n }, debounceMs)\n }\n\n const shutdown = async (exitCode: number) => {\n if (shuttingDown) {\n return\n }\n\n shuttingDown = true\n clearTimeout(restartTimer)\n server.watcher.off('all', onWatcherEvent)\n await server.watcher.unwatch(platform)\n await server.close()\n await stopCapProcess(currentCapProcess)\n process.exit(exitCode)\n }\n\n server.watcher.add(platform)\n server.watcher.on('all', onWatcherEvent)\n\n process.once('SIGINT', () => {\n void shutdown(0)\n })\n process.once('SIGTERM', () => {\n void shutdown(0)\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Output } from 'tinyexec'\n\nimport process from 'node:process'\n\nimport { extname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { x } from 'tinyexec'\n\nimport { parseCapacitorPlatform } from './native'\n\nexport type { CapacitorPlatform } from './native'\n\nexport interface RunCapViteOptions {\n cwd?: string\n}\n\ninterface PreparedViteLaunch {\n baseConfigFile?: string\n configLoader?: 'bundle' | 'native' | 'runner'\n projectRoot: string\n viteArgs: string[]\n wrapperConfigFile: string\n}\n\ninterface ParsedViteArg {\n baseConfigFile?: string\n configLoader?: 'bundle' | 'native' | 'runner'\n consumedArgs: number\n forwardedArgs: string[]\n}\n\nfunction resolveWrapperConfigFile(): string {\n const currentModulePath = fileURLToPath(import.meta.url)\n const wrapperExtension = extname(currentModulePath) === '.ts' ? '.ts' : '.mjs'\n return fileURLToPath(new URL(`./vite-wrapper-config${wrapperExtension}`, import.meta.url))\n}\n\nfunction parseViteConfigLoader(value: string | undefined): 'bundle' | 'native' | 'runner' | undefined {\n if (value === 'bundle' || value === 'native' || value === 'runner') {\n return value\n }\n\n return undefined\n}\n\nfunction resolveConfigPath(cwd: string, value: string): string {\n return resolve(cwd, value)\n}\n\nfunction readRequiredOptionValue(viteArgs: string[], index: number, optionName: string): string {\n const value = viteArgs[index + 1]\n if (!value) {\n throw new Error(`Missing value for \\`${optionName}\\`.`)\n }\n\n return value\n}\n\nfunction parseConfigArg(viteArgs: string[], index: number, cwd: string): ParsedViteArg | null {\n const arg = viteArgs[index]\n\n // NOTICE: Vite only accepts one `--config` entrypoint. cap-vite consumes that slot\n // for its wrapper config, then loads the user config from inside the wrapper.\n if (arg === '--config' || arg === '-c') {\n return {\n baseConfigFile: resolveConfigPath(cwd, readRequiredOptionValue(viteArgs, index, '--config')),\n consumedArgs: 2,\n forwardedArgs: [],\n }\n }\n\n if (arg.startsWith('--config=')) {\n return {\n baseConfigFile: resolveConfigPath(cwd, arg.slice('--config='.length)),\n consumedArgs: 1,\n forwardedArgs: [],\n }\n }\n\n return null\n}\n\nfunction parseConfigLoaderArg(viteArgs: string[], index: number): ParsedViteArg | null {\n const arg = viteArgs[index]\n\n if (arg === '--configLoader') {\n const value = readRequiredOptionValue(viteArgs, index, '--configLoader')\n\n return {\n configLoader: parseViteConfigLoader(value),\n consumedArgs: 2,\n forwardedArgs: [arg, value],\n }\n }\n\n if (arg.startsWith('--configLoader=')) {\n return {\n configLoader: parseViteConfigLoader(arg.slice('--configLoader='.length)),\n consumedArgs: 1,\n forwardedArgs: [arg],\n }\n }\n\n return null\n}\n\nfunction parseViteArg(viteArgs: string[], index: number, cwd: string): ParsedViteArg {\n return parseConfigArg(viteArgs, index, cwd)\n ?? parseConfigLoaderArg(viteArgs, index)\n ?? {\n consumedArgs: 1,\n forwardedArgs: [viteArgs[index]],\n }\n}\n\nfunction resolveProjectRoot(viteArgs: string[], cwd: string): string {\n const firstArg = viteArgs[0]\n\n return firstArg && !firstArg.startsWith('-')\n ? resolve(cwd, firstArg)\n : cwd\n}\n\nexport function prepareCapViteLaunch(viteArgs: string[], cwd: string = process.cwd()): PreparedViteLaunch {\n const resolvedCwd = resolve(cwd)\n const projectRoot = resolveProjectRoot(viteArgs, resolvedCwd)\n\n let baseConfigFile: string | undefined\n let configLoader: 'bundle' | 'native' | 'runner' | undefined\n const forwardedViteArgs: string[] = []\n\n for (let index = 0; index < viteArgs.length;) {\n const parsedArg = parseViteArg(viteArgs, index, resolvedCwd)\n\n baseConfigFile = parsedArg.baseConfigFile ?? baseConfigFile\n configLoader = parsedArg.configLoader ?? configLoader\n forwardedViteArgs.push(...parsedArg.forwardedArgs)\n index += parsedArg.consumedArgs\n }\n\n return {\n baseConfigFile,\n configLoader,\n projectRoot,\n viteArgs: forwardedViteArgs,\n wrapperConfigFile: resolveWrapperConfigFile(),\n }\n}\n\nexport async function runCapVite(\n viteArgs: string[],\n capArgs: string[],\n options: RunCapViteOptions = {},\n): Promise<Output> {\n if (!parseCapacitorPlatform(capArgs[0])) {\n throw new Error('The first `cap run` argument must be `ios` or `android`.')\n }\n\n const cwd = resolve(options.cwd ?? process.cwd())\n const prepared = prepareCapViteLaunch(viteArgs, cwd)\n\n return await x('vite', ['--config', prepared.wrapperConfigFile, ...prepared.viteArgs], {\n throwOnError: false,\n nodeOptions: {\n cwd,\n env: {\n CAP_VITE_BASE_CONFIG: prepared.baseConfigFile ?? '',\n CAP_VITE_CAP_ARGS_JSON: JSON.stringify(capArgs),\n CAP_VITE_CONFIG_LOADER: prepared.configLoader ?? '',\n CAP_VITE_ROOT: prepared.projectRoot,\n },\n stdio: 'inherit',\n },\n })\n}\n"],"mappings":";;;;;;AAgCA,SAAS,2BAAmC;CAE1C,MAAM,mBAAmB,QADC,cAAc,OAAO,KAAK,IAAI,CACL,KAAK,QAAQ,QAAQ;AACxE,QAAO,cAAc,IAAI,IAAI,wBAAwB,oBAAoB,OAAO,KAAK,IAAI,CAAC;;AAG5F,SAAS,sBAAsB,OAAuE;AACpG,KAAI,UAAU,YAAY,UAAU,YAAY,UAAU,SACxD,QAAO;;AAMX,SAAS,kBAAkB,KAAa,OAAuB;AAC7D,QAAO,QAAQ,KAAK,MAAM;;AAG5B,SAAS,wBAAwB,UAAoB,OAAe,YAA4B;CAC9F,MAAM,QAAQ,SAAS,QAAQ;AAC/B,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,uBAAuB,WAAW,KAAK;AAGzD,QAAO;;AAGT,SAAS,eAAe,UAAoB,OAAe,KAAmC;CAC5F,MAAM,MAAM,SAAS;AAIrB,KAAI,QAAQ,cAAc,QAAQ,KAChC,QAAO;EACL,gBAAgB,kBAAkB,KAAK,wBAAwB,UAAU,OAAO,WAAW,CAAC;EAC5F,cAAc;EACd,eAAe,EAAE;EAClB;AAGH,KAAI,IAAI,WAAW,YAAY,CAC7B,QAAO;EACL,gBAAgB,kBAAkB,KAAK,IAAI,MAAM,EAAmB,CAAC;EACrE,cAAc;EACd,eAAe,EAAE;EAClB;AAGH,QAAO;;AAGT,SAAS,qBAAqB,UAAoB,OAAqC;CACrF,MAAM,MAAM,SAAS;AAErB,KAAI,QAAQ,kBAAkB;EAC5B,MAAM,QAAQ,wBAAwB,UAAU,OAAO,iBAAiB;AAExE,SAAO;GACL,cAAc,sBAAsB,MAAM;GAC1C,cAAc;GACd,eAAe,CAAC,KAAK,MAAM;GAC5B;;AAGH,KAAI,IAAI,WAAW,kBAAkB,CACnC,QAAO;EACL,cAAc,sBAAsB,IAAI,MAAM,GAAyB,CAAC;EACxE,cAAc;EACd,eAAe,CAAC,IAAI;EACrB;AAGH,QAAO;;AAGT,SAAS,aAAa,UAAoB,OAAe,KAA4B;AACnF,QAAO,eAAe,UAAU,OAAO,IAAI,IACtC,qBAAqB,UAAU,MAAM,IACrC;EACD,cAAc;EACd,eAAe,CAAC,SAAS,OAAO;EACjC;;AAGL,SAAS,mBAAmB,UAAoB,KAAqB;CACnE,MAAM,WAAW,SAAS;AAE1B,QAAO,YAAY,CAAC,SAAS,WAAW,IAAI,GACxC,QAAQ,KAAK,SAAS,GACtB;;AAGN,SAAgB,qBAAqB,UAAoB,MAAc,QAAQ,KAAK,EAAsB;CACxG,MAAM,cAAc,QAAQ,IAAI;CAChC,MAAM,cAAc,mBAAmB,UAAU,YAAY;CAE7D,IAAI;CACJ,IAAI;CACJ,MAAM,oBAA8B,EAAE;AAEtC,MAAK,IAAI,QAAQ,GAAG,QAAQ,SAAS,SAAS;EAC5C,MAAM,YAAY,aAAa,UAAU,OAAO,YAAY;AAE5D,mBAAiB,UAAU,kBAAkB;AAC7C,iBAAe,UAAU,gBAAgB;AACzC,oBAAkB,KAAK,GAAG,UAAU,cAAc;AAClD,WAAS,UAAU;;AAGrB,QAAO;EACL;EACA;EACA;EACA,UAAU;EACV,mBAAmB,0BAA0B;EAC9C;;AAGH,eAAsB,WACpB,UACA,SACA,UAA6B,EAAE,EACd;AACjB,KAAI,CAAC,uBAAuB,QAAQ,GAAG,CACrC,OAAM,IAAI,MAAM,2DAA2D;CAG7E,MAAM,MAAM,QAAQ,QAAQ,OAAO,QAAQ,KAAK,CAAC;CACjD,MAAM,WAAW,qBAAqB,UAAU,IAAI;AAEpD,QAAO,MAAM,EAAE,QAAQ;EAAC;EAAY,SAAS;EAAmB,GAAG,SAAS;EAAS,EAAE;EACrF,cAAc;EACd,aAAa;GACX;GACA,KAAK;IACH,sBAAsB,SAAS,kBAAkB;IACjD,wBAAwB,KAAK,UAAU,QAAQ;IAC/C,wBAAwB,SAAS,gBAAgB;IACjD,eAAe,SAAS;IACzB;GACD,OAAO;GACR;EACF,CAAC"}
@@ -0,0 +1,125 @@
1
+ import process from "node:process";
2
+ import { basename, extname, relative, resolve, sep } from "node:path";
3
+ //#region src/native.ts
4
+ const nativeExtensionsByPlatform = {
5
+ ios: new Set([
6
+ ".entitlements",
7
+ ".h",
8
+ ".hpp",
9
+ ".m",
10
+ ".mm",
11
+ ".pbxproj",
12
+ ".plist",
13
+ ".storyboard",
14
+ ".strings",
15
+ ".swift",
16
+ ".xcodeproj",
17
+ ".xcconfig",
18
+ ".xcscheme",
19
+ ".xib"
20
+ ]),
21
+ android: new Set([
22
+ ".gradle",
23
+ ".java",
24
+ ".json",
25
+ ".kts",
26
+ ".kt",
27
+ ".properties",
28
+ ".xml"
29
+ ])
30
+ };
31
+ const nativeNamesByPlatform = {
32
+ ios: new Set([
33
+ "Podfile",
34
+ "Podfile.lock",
35
+ "project.pbxproj"
36
+ ]),
37
+ android: new Set([
38
+ "AndroidManifest.xml",
39
+ "build.gradle",
40
+ "build.gradle.kts",
41
+ "gradle.properties",
42
+ "settings.gradle",
43
+ "settings.gradle.kts"
44
+ ])
45
+ };
46
+ const ignoredNames = new Set(["capacitor.config.json"]);
47
+ const ignoredPathSegments = new Set([
48
+ ".gradle",
49
+ "DerivedData",
50
+ "Pods",
51
+ "build",
52
+ "xcuserdata"
53
+ ]);
54
+ const ignoredPathPrefixesByPlatform = {
55
+ ios: [["App", "CapApp-SPM"]],
56
+ android: [
57
+ [
58
+ "app",
59
+ "src",
60
+ "main",
61
+ "assets",
62
+ "public"
63
+ ],
64
+ [
65
+ "app",
66
+ "src",
67
+ "main",
68
+ "assets",
69
+ "capacitor.plugins.json"
70
+ ],
71
+ [
72
+ "app",
73
+ "src",
74
+ "main",
75
+ "res",
76
+ "xml",
77
+ "config.xml"
78
+ ],
79
+ ["app", "capacitor.build.gradle"],
80
+ ["capacitor-cordova-android-plugins"],
81
+ ["capacitor.settings.gradle"]
82
+ ]
83
+ };
84
+ function parseCapacitorPlatform(value) {
85
+ return value === "android" || value === "ios" ? value : null;
86
+ }
87
+ function hasCapacitorTargetArg(capArgs) {
88
+ return capArgs.some((arg, index) => arg === "--target" || index > 0 && arg.startsWith("--target="));
89
+ }
90
+ function resolveCapRunArgs(capArgs, env = process.env) {
91
+ if (capArgs.length === 0 || hasCapacitorTargetArg(capArgs)) return capArgs;
92
+ const [platformArg, ...rest] = capArgs;
93
+ const platform = parseCapacitorPlatform(platformArg);
94
+ let target;
95
+ if (platform === "ios") target = env.CAPACITOR_DEVICE_ID_IOS;
96
+ else if (platform === "android") target = env.CAPACITOR_DEVICE_ID_ANDROID;
97
+ if (!target) return capArgs;
98
+ return [
99
+ platformArg,
100
+ "--target",
101
+ target,
102
+ ...rest
103
+ ];
104
+ }
105
+ function pickServerUrl(server) {
106
+ const url = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0];
107
+ if (!url) throw new Error("Vite did not expose a reachable dev server URL.");
108
+ return new URL(url);
109
+ }
110
+ function shouldRestartForNativeChange(file, platform, cwd) {
111
+ const absoluteFile = resolve(cwd, file);
112
+ const platformRoot = resolve(cwd, platform);
113
+ if (!absoluteFile.startsWith(`${platformRoot}${sep}`) && absoluteFile !== platformRoot) return false;
114
+ const fileName = basename(absoluteFile);
115
+ if (ignoredNames.has(fileName)) return false;
116
+ if (absoluteFile.split(sep).some((segment) => ignoredPathSegments.has(segment))) return false;
117
+ const relativeSegments = relative(platformRoot, absoluteFile).split(sep).filter(Boolean);
118
+ if (ignoredPathPrefixesByPlatform[platform].some((prefix) => prefix.every((segment, index) => relativeSegments[index] === segment))) return false;
119
+ if (nativeNamesByPlatform[platform].has(fileName)) return true;
120
+ return nativeExtensionsByPlatform[platform].has(extname(fileName).toLowerCase());
121
+ }
122
+ //#endregion
123
+ export { shouldRestartForNativeChange as i, pickServerUrl as n, resolveCapRunArgs as r, parseCapacitorPlatform as t };
124
+
125
+ //# sourceMappingURL=native-hZ5d7ukC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-hZ5d7ukC.mjs","names":[],"sources":["../src/native.ts"],"sourcesContent":["import type { ViteDevServer } from 'vite'\n\nimport process from 'node:process'\n\nimport { basename, extname, relative, resolve, sep } from 'node:path'\n\nexport type CapacitorPlatform = 'android' | 'ios'\n\nconst nativeExtensionsByPlatform: Record<CapacitorPlatform, Set<string>> = {\n ios: new Set([\n '.entitlements',\n '.h',\n '.hpp',\n '.m',\n '.mm',\n '.pbxproj',\n '.plist',\n '.storyboard',\n '.strings',\n '.swift',\n '.xcodeproj',\n '.xcconfig',\n '.xcscheme',\n '.xib',\n ]),\n android: new Set([\n '.gradle',\n '.java',\n '.json',\n '.kts',\n '.kt',\n '.properties',\n '.xml',\n ]),\n}\n\nconst nativeNamesByPlatform: Record<CapacitorPlatform, Set<string>> = {\n ios: new Set([\n 'Podfile',\n 'Podfile.lock',\n 'project.pbxproj',\n ]),\n android: new Set([\n 'AndroidManifest.xml',\n 'build.gradle',\n 'build.gradle.kts',\n 'gradle.properties',\n 'settings.gradle',\n 'settings.gradle.kts',\n ]),\n}\n\nconst ignoredNames = new Set([\n 'capacitor.config.json',\n])\n\nconst ignoredPathSegments = new Set([\n '.gradle',\n 'DerivedData',\n 'Pods',\n 'build',\n 'xcuserdata',\n])\n\nconst ignoredPathPrefixesByPlatform: Record<CapacitorPlatform, string[][]> = {\n ios: [\n ['App', 'CapApp-SPM'],\n ],\n android: [\n ['app', 'src', 'main', 'assets', 'public'],\n ['app', 'src', 'main', 'assets', 'capacitor.plugins.json'],\n ['app', 'src', 'main', 'res', 'xml', 'config.xml'],\n ['app', 'capacitor.build.gradle'],\n ['capacitor-cordova-android-plugins'],\n ['capacitor.settings.gradle'],\n ],\n}\n\nexport function parseCapacitorPlatform(value: string | undefined): CapacitorPlatform | null {\n return value === 'android' || value === 'ios' ? value : null\n}\n\nexport function hasCapacitorTargetArg(capArgs: string[]): boolean {\n return capArgs.some((arg, index) => arg === '--target' || (index > 0 && arg.startsWith('--target=')))\n}\n\nexport function resolveCapRunArgs(capArgs: string[], env: NodeJS.ProcessEnv = process.env): string[] {\n if (capArgs.length === 0 || hasCapacitorTargetArg(capArgs)) {\n return capArgs\n }\n\n const [platformArg, ...rest] = capArgs\n const platform = parseCapacitorPlatform(platformArg)\n let target: string | undefined\n if (platform === 'ios') {\n target = env.CAPACITOR_DEVICE_ID_IOS\n }\n else if (platform === 'android') {\n target = env.CAPACITOR_DEVICE_ID_ANDROID\n }\n\n if (!target) {\n return capArgs\n }\n\n return [platformArg, '--target', target, ...rest]\n}\n\nexport function pickServerUrl(server: Pick<ViteDevServer, 'resolvedUrls'>): URL {\n const url = server.resolvedUrls?.network?.[0] ?? server.resolvedUrls?.local?.[0]\n\n if (!url) {\n throw new Error('Vite did not expose a reachable dev server URL.')\n }\n\n return new URL(url)\n}\n\nexport function shouldRestartForNativeChange(file: string, platform: CapacitorPlatform, cwd: string): boolean {\n const absoluteFile = resolve(cwd, file)\n const platformRoot = resolve(cwd, platform)\n\n if (!absoluteFile.startsWith(`${platformRoot}${sep}`) && absoluteFile !== platformRoot) {\n return false\n }\n\n const fileName = basename(absoluteFile)\n\n if (ignoredNames.has(fileName)) {\n return false\n }\n\n const segments = absoluteFile.split(sep)\n if (segments.some(segment => ignoredPathSegments.has(segment))) {\n return false\n }\n\n const relativeFile = relative(platformRoot, absoluteFile)\n const relativeSegments = relativeFile.split(sep).filter(Boolean)\n\n if (ignoredPathPrefixesByPlatform[platform].some(prefix =>\n prefix.every((segment, index) => relativeSegments[index] === segment),\n )) {\n // NOTICE: Capacitor regenerates ios/App/CapApp-SPM/Package.swift during `cap run`.\n // It also rewrites several generated Android files and plugin trees during `cap update`.\n // Treating those generated outputs as native source changes causes an infinite restart loop.\n return false\n }\n\n if (nativeNamesByPlatform[platform].has(fileName)) {\n return true\n }\n\n return nativeExtensionsByPlatform[platform].has(extname(fileName).toLowerCase())\n}\n"],"mappings":";;;AAQA,MAAM,6BAAqE;CACzE,KAAK,IAAI,IAAI;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,SAAS,IAAI,IAAI;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACH;AAED,MAAM,wBAAgE;CACpE,KAAK,IAAI,IAAI;EACX;EACA;EACA;EACD,CAAC;CACF,SAAS,IAAI,IAAI;EACf;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACH;AAED,MAAM,eAAe,IAAI,IAAI,CAC3B,wBACD,CAAC;AAEF,MAAM,sBAAsB,IAAI,IAAI;CAClC;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAM,gCAAuE;CAC3E,KAAK,CACH,CAAC,OAAO,aAAa,CACtB;CACD,SAAS;EACP;GAAC;GAAO;GAAO;GAAQ;GAAU;GAAS;EAC1C;GAAC;GAAO;GAAO;GAAQ;GAAU;GAAyB;EAC1D;GAAC;GAAO;GAAO;GAAQ;GAAO;GAAO;GAAa;EAClD,CAAC,OAAO,yBAAyB;EACjC,CAAC,oCAAoC;EACrC,CAAC,4BAA4B;EAC9B;CACF;AAED,SAAgB,uBAAuB,OAAqD;AAC1F,QAAO,UAAU,aAAa,UAAU,QAAQ,QAAQ;;AAG1D,SAAgB,sBAAsB,SAA4B;AAChE,QAAO,QAAQ,MAAM,KAAK,UAAU,QAAQ,cAAe,QAAQ,KAAK,IAAI,WAAW,YAAY,CAAE;;AAGvG,SAAgB,kBAAkB,SAAmB,MAAyB,QAAQ,KAAe;AACnG,KAAI,QAAQ,WAAW,KAAK,sBAAsB,QAAQ,CACxD,QAAO;CAGT,MAAM,CAAC,aAAa,GAAG,QAAQ;CAC/B,MAAM,WAAW,uBAAuB,YAAY;CACpD,IAAI;AACJ,KAAI,aAAa,MACf,UAAS,IAAI;UAEN,aAAa,UACpB,UAAS,IAAI;AAGf,KAAI,CAAC,OACH,QAAO;AAGT,QAAO;EAAC;EAAa;EAAY;EAAQ,GAAG;EAAK;;AAGnD,SAAgB,cAAc,QAAkD;CAC9E,MAAM,MAAM,OAAO,cAAc,UAAU,MAAM,OAAO,cAAc,QAAQ;AAE9E,KAAI,CAAC,IACH,OAAM,IAAI,MAAM,kDAAkD;AAGpE,QAAO,IAAI,IAAI,IAAI;;AAGrB,SAAgB,6BAA6B,MAAc,UAA6B,KAAsB;CAC5G,MAAM,eAAe,QAAQ,KAAK,KAAK;CACvC,MAAM,eAAe,QAAQ,KAAK,SAAS;AAE3C,KAAI,CAAC,aAAa,WAAW,GAAG,eAAe,MAAM,IAAI,iBAAiB,aACxE,QAAO;CAGT,MAAM,WAAW,SAAS,aAAa;AAEvC,KAAI,aAAa,IAAI,SAAS,CAC5B,QAAO;AAIT,KADiB,aAAa,MAAM,IAAI,CAC3B,MAAK,YAAW,oBAAoB,IAAI,QAAQ,CAAC,CAC5D,QAAO;CAIT,MAAM,mBADe,SAAS,cAAc,aAAa,CACnB,MAAM,IAAI,CAAC,OAAO,QAAQ;AAEhE,KAAI,8BAA8B,UAAU,MAAK,WAC/C,OAAO,OAAO,SAAS,UAAU,iBAAiB,WAAW,QAAQ,CACtE,CAIC,QAAO;AAGT,KAAI,sBAAsB,UAAU,IAAI,SAAS,CAC/C,QAAO;AAGT,QAAO,2BAA2B,UAAU,IAAI,QAAQ,SAAS,CAAC,aAAa,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region src/vite-plugin.d.ts
4
+ interface CapVitePluginOptions {
5
+ capArgs: string[];
6
+ }
7
+ declare function capVitePlugin(options: CapVitePluginOptions): Plugin;
8
+ //#endregion
9
+ export { CapVitePluginOptions, capVitePlugin };
10
+ //# sourceMappingURL=vite-plugin.d.mts.map
@@ -0,0 +1,146 @@
1
+ import { i as shouldRestartForNativeChange, n as pickServerUrl, r as resolveCapRunArgs, t as parseCapacitorPlatform } from "./native-hZ5d7ukC.mjs";
2
+ import process from "node:process";
3
+ import { resolve } from "node:path";
4
+ import { x } from "tinyexec";
5
+ import * as readline from "node:readline";
6
+ //#region src/vite-plugin.ts
7
+ async function stopCapProcess(current) {
8
+ if (!current) return;
9
+ current.kill("SIGINT");
10
+ try {
11
+ await current;
12
+ } catch {}
13
+ }
14
+ function startCapProcess(cwd, capArgs, url) {
15
+ return x("cap", ["run", ...capArgs], {
16
+ throwOnError: false,
17
+ nodeOptions: {
18
+ cwd,
19
+ env: { CAPACITOR_DEV_SERVER_URL: url.toString() },
20
+ stdio: [
21
+ "ignore",
22
+ "inherit",
23
+ "inherit"
24
+ ]
25
+ }
26
+ });
27
+ }
28
+ function bindCapViteShortcuts(onRestart, onShutdown) {
29
+ if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== "function") return () => {};
30
+ process.stdin.resume();
31
+ process.stdin.setEncoding("utf8");
32
+ readline.emitKeypressEvents(process.stdin);
33
+ const shouldRestoreRawMode = !process.stdin.isRaw;
34
+ if (shouldRestoreRawMode) process.stdin.setRawMode(true);
35
+ async function shutdownFromShortcut() {
36
+ try {
37
+ await onShutdown();
38
+ } finally {
39
+ if (shouldRestoreRawMode) process.stdin.setRawMode(false);
40
+ process.kill(process.pid, "SIGINT");
41
+ }
42
+ }
43
+ const onKeyPress = (input, key) => {
44
+ if (key.ctrl && key.name === "c") {
45
+ shutdownFromShortcut();
46
+ return;
47
+ }
48
+ const keyName = key.name?.toLowerCase() ?? input.toLowerCase();
49
+ if (!key.ctrl && !key.meta && keyName === "r") onRestart();
50
+ };
51
+ process.stdin.on("keypress", onKeyPress);
52
+ return () => {
53
+ process.stdin.off("keypress", onKeyPress);
54
+ if (shouldRestoreRawMode) process.stdin.setRawMode(false);
55
+ };
56
+ }
57
+ function capVitePlugin(options) {
58
+ const resolvedCapArgs = resolveCapRunArgs(options.capArgs);
59
+ const platform = parseCapacitorPlatform(resolvedCapArgs[0]);
60
+ if (!platform) throw new Error("The first `cap run` argument must be `ios` or `android`.");
61
+ const resolvedPlatform = platform;
62
+ return {
63
+ apply: "serve",
64
+ name: "cap-vite:run-capacitor",
65
+ configureServer(server) {
66
+ const cwd = resolve(server.config.root);
67
+ const platformRoot = resolve(cwd, resolvedPlatform);
68
+ const debounceMs = 300;
69
+ const logger = server.config.logger;
70
+ let currentCapProcess;
71
+ let restartTask;
72
+ let queuedRestartReason;
73
+ let disposeShortcut;
74
+ let shuttingDown = false;
75
+ let restartTimer;
76
+ function launchCapProcess() {
77
+ currentCapProcess = startCapProcess(cwd, resolvedCapArgs, pickServerUrl(server));
78
+ currentCapProcess.then(() => {
79
+ logger.info(`[cap-vite] Ran "cap run ${resolvedCapArgs.join(" ")}". Press R to re-run. Press Ctrl+C to exit.`);
80
+ });
81
+ }
82
+ function requestRestart(reason) {
83
+ if (shuttingDown) return;
84
+ queuedRestartReason = reason;
85
+ if (!restartTask) restartTask = flushPendingRestarts();
86
+ }
87
+ async function flushPendingRestarts() {
88
+ try {
89
+ while (queuedRestartReason) {
90
+ const activeReason = queuedRestartReason;
91
+ queuedRestartReason = void 0;
92
+ if (shuttingDown) return;
93
+ logger.info(`[cap-vite] ${activeReason}. Re-running "cap run ${resolvedCapArgs.join(" ")}".`);
94
+ const previous = currentCapProcess;
95
+ currentCapProcess = void 0;
96
+ await stopCapProcess(previous);
97
+ if (shuttingDown) return;
98
+ launchCapProcess();
99
+ }
100
+ } catch (error) {
101
+ logger.error(`[cap-vite] ${error instanceof Error ? error.message : String(error)}`);
102
+ await shutdown();
103
+ } finally {
104
+ restartTask = void 0;
105
+ }
106
+ }
107
+ function onWatcherEvent(_event, file) {
108
+ if (!shouldRestartForNativeChange(file, resolvedPlatform, cwd)) return;
109
+ clearTimeout(restartTimer);
110
+ restartTimer = setTimeout(() => {
111
+ requestRestart(`native file changed: ${resolve(cwd, file)}`);
112
+ }, debounceMs);
113
+ }
114
+ function handleShutdownRequest() {
115
+ shutdown();
116
+ }
117
+ async function shutdown() {
118
+ if (shuttingDown) return;
119
+ shuttingDown = true;
120
+ clearTimeout(restartTimer);
121
+ queuedRestartReason = void 0;
122
+ const disposeBoundShortcut = disposeShortcut;
123
+ disposeShortcut = void 0;
124
+ disposeBoundShortcut?.();
125
+ server.watcher.off("all", onWatcherEvent);
126
+ process.off("SIGINT", handleShutdownRequest);
127
+ process.off("SIGTERM", handleShutdownRequest);
128
+ await server.watcher.unwatch(platformRoot);
129
+ await stopCapProcess(currentCapProcess);
130
+ }
131
+ server.watcher.add(platformRoot);
132
+ server.watcher.on("all", onWatcherEvent);
133
+ server.httpServer?.once("listening", () => {
134
+ launchCapProcess();
135
+ disposeShortcut = bindCapViteShortcuts(() => requestRestart("manual restart requested"), shutdown);
136
+ });
137
+ server.httpServer?.once("close", handleShutdownRequest);
138
+ process.once("SIGINT", handleShutdownRequest);
139
+ process.once("SIGTERM", handleShutdownRequest);
140
+ }
141
+ };
142
+ }
143
+ //#endregion
144
+ export { capVitePlugin };
145
+
146
+ //# sourceMappingURL=vite-plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.mjs","names":[],"sources":["../src/vite-plugin.ts"],"sourcesContent":["import type { Result } from 'tinyexec'\nimport type { Plugin } from 'vite'\n\nimport type { CapacitorPlatform } from './native'\n\nimport process from 'node:process'\n\nimport { resolve } from 'node:path'\n\nimport * as readline from 'node:readline'\n\nimport { x } from 'tinyexec'\n\nimport { parseCapacitorPlatform, pickServerUrl, resolveCapRunArgs, shouldRestartForNativeChange } from './native'\n\nexport interface CapVitePluginOptions {\n capArgs: string[]\n}\n\nasync function stopCapProcess(current: Result | undefined) {\n if (!current) {\n return\n }\n\n current.kill('SIGINT')\n\n try {\n await current\n }\n catch {\n // tinyexec rejects when a process is stopped during a restart.\n }\n}\n\nfunction startCapProcess(cwd: string, capArgs: string[], url: URL) {\n return x('cap', ['run', ...capArgs], {\n throwOnError: false,\n nodeOptions: {\n cwd,\n env: {\n CAPACITOR_DEV_SERVER_URL: url.toString(),\n },\n // NOTICE: cap-vite owns the terminal shortcuts, so cap run should not\n // consume stdin while still mirroring its stdout/stderr to the console.\n stdio: ['ignore', 'inherit', 'inherit'],\n },\n })\n}\n\nfunction bindCapViteShortcuts(\n onRestart: () => void,\n onShutdown: () => Promise<void>,\n) {\n if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== 'function') {\n return () => {}\n }\n\n process.stdin.resume()\n process.stdin.setEncoding('utf8')\n readline.emitKeypressEvents(process.stdin)\n\n const shouldRestoreRawMode = !process.stdin.isRaw\n if (shouldRestoreRawMode) {\n process.stdin.setRawMode(true)\n }\n\n async function shutdownFromShortcut() {\n try {\n await onShutdown()\n }\n finally {\n if (shouldRestoreRawMode) {\n process.stdin.setRawMode(false)\n }\n\n process.kill(process.pid, 'SIGINT')\n }\n }\n\n const onKeyPress = (input: string, key: readline.Key) => {\n if (key.ctrl && key.name === 'c') {\n void shutdownFromShortcut()\n return\n }\n\n const keyName = key.name?.toLowerCase() ?? input.toLowerCase()\n if (!key.ctrl && !key.meta && keyName === 'r') {\n onRestart()\n }\n }\n\n process.stdin.on('keypress', onKeyPress)\n\n return () => {\n process.stdin.off('keypress', onKeyPress)\n\n if (shouldRestoreRawMode) {\n process.stdin.setRawMode(false)\n }\n }\n}\n\nexport function capVitePlugin(options: CapVitePluginOptions): Plugin {\n const resolvedCapArgs = resolveCapRunArgs(options.capArgs)\n const platform = parseCapacitorPlatform(resolvedCapArgs[0])\n if (!platform) {\n throw new Error('The first `cap run` argument must be `ios` or `android`.')\n }\n const resolvedPlatform: CapacitorPlatform = platform\n\n return {\n apply: 'serve',\n name: 'cap-vite:run-capacitor',\n configureServer(server) {\n const cwd = resolve(server.config.root)\n const platformRoot = resolve(cwd, resolvedPlatform)\n const debounceMs = 300\n const logger = server.config.logger\n\n let currentCapProcess: Result | undefined\n let restartTask: Promise<void> | undefined\n let queuedRestartReason: string | undefined\n let disposeShortcut: (() => void) | undefined\n let shuttingDown = false\n let restartTimer: NodeJS.Timeout | undefined\n\n function launchCapProcess() {\n const url = pickServerUrl(server)\n currentCapProcess = startCapProcess(cwd, resolvedCapArgs, url)\n currentCapProcess.then(() => {\n logger.info(`[cap-vite] Ran \"cap run ${resolvedCapArgs.join(' ')}\". Press R to re-run. Press Ctrl+C to exit.`)\n })\n }\n\n function requestRestart(reason: string) {\n if (shuttingDown) {\n return\n }\n\n queuedRestartReason = reason\n if (!restartTask) {\n restartTask = flushPendingRestarts()\n }\n }\n\n async function flushPendingRestarts() {\n try {\n while (queuedRestartReason) {\n const activeReason = queuedRestartReason\n queuedRestartReason = undefined\n\n if (shuttingDown) {\n return\n }\n\n logger.info(`[cap-vite] ${activeReason}. Re-running \"cap run ${resolvedCapArgs.join(' ')}\".`)\n const previous = currentCapProcess\n currentCapProcess = undefined\n await stopCapProcess(previous)\n\n if (shuttingDown) {\n return\n }\n\n launchCapProcess()\n }\n }\n catch (error) {\n logger.error(`[cap-vite] ${error instanceof Error ? error.message : String(error)}`)\n await shutdown()\n }\n finally {\n restartTask = undefined\n }\n }\n\n function onWatcherEvent(_event, file) {\n if (!shouldRestartForNativeChange(file, resolvedPlatform, cwd)) {\n return\n }\n\n clearTimeout(restartTimer)\n restartTimer = setTimeout(() => {\n requestRestart(`native file changed: ${resolve(cwd, file)}`)\n }, debounceMs)\n }\n\n function handleShutdownRequest() {\n void shutdown()\n }\n\n async function shutdown() {\n if (shuttingDown) {\n return\n }\n\n shuttingDown = true\n clearTimeout(restartTimer)\n queuedRestartReason = undefined\n const disposeBoundShortcut = disposeShortcut\n disposeShortcut = undefined\n disposeBoundShortcut?.()\n server.watcher.off('all', onWatcherEvent)\n process.off('SIGINT', handleShutdownRequest)\n process.off('SIGTERM', handleShutdownRequest)\n await server.watcher.unwatch(platformRoot)\n await stopCapProcess(currentCapProcess)\n }\n\n server.watcher.add(platformRoot)\n server.watcher.on('all', onWatcherEvent)\n\n server.httpServer?.once('listening', () => {\n launchCapProcess()\n disposeShortcut = bindCapViteShortcuts(() => requestRestart('manual restart requested'), shutdown)\n })\n server.httpServer?.once('close', handleShutdownRequest)\n process.once('SIGINT', handleShutdownRequest)\n process.once('SIGTERM', handleShutdownRequest)\n },\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import * as vite from "vite";
2
+
3
+ //#region src/vite-wrapper-config.d.ts
4
+ declare const _default: vite.UserConfigFnPromise;
5
+ //#endregion
6
+ export { _default as default };
7
+ //# sourceMappingURL=vite-wrapper-config.d.mts.map
@@ -0,0 +1,23 @@
1
+ import { capVitePlugin } from "./vite-plugin.mjs";
2
+ import process from "node:process";
3
+ import { defineConfig, loadConfigFromFile, mergeConfig } from "vite";
4
+ //#region src/vite-wrapper-config.ts
5
+ function parseCapArgs() {
6
+ const value = process.env.CAP_VITE_CAP_ARGS_JSON;
7
+ if (!value) return [];
8
+ const parsed = JSON.parse(value);
9
+ if (!Array.isArray(parsed) || parsed.some((arg) => typeof arg !== "string")) throw new Error("CAP_VITE_CAP_ARGS_JSON must be a JSON string array.");
10
+ return parsed;
11
+ }
12
+ function parseConfigLoader() {
13
+ const value = process.env.CAP_VITE_CONFIG_LOADER;
14
+ if (value === "bundle" || value === "native" || value === "runner") return value;
15
+ }
16
+ var vite_wrapper_config_default = defineConfig(async (env) => {
17
+ const root = process.env.CAP_VITE_ROOT ?? process.cwd();
18
+ return mergeConfig((await loadConfigFromFile(env, process.env.CAP_VITE_BASE_CONFIG || void 0, root, void 0, void 0, parseConfigLoader()))?.config ?? {}, { plugins: [capVitePlugin({ capArgs: parseCapArgs() })] });
19
+ });
20
+ //#endregion
21
+ export { vite_wrapper_config_default as default };
22
+
23
+ //# sourceMappingURL=vite-wrapper-config.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-wrapper-config.mjs","names":[],"sources":["../src/vite-wrapper-config.ts"],"sourcesContent":["import process from 'node:process'\n\nimport { defineConfig, loadConfigFromFile, mergeConfig } from 'vite'\n\nimport { capVitePlugin } from './vite-plugin'\n\nfunction parseCapArgs(): string[] {\n const value = process.env.CAP_VITE_CAP_ARGS_JSON\n if (!value) {\n return []\n }\n\n const parsed = JSON.parse(value)\n if (!Array.isArray(parsed) || parsed.some(arg => typeof arg !== 'string')) {\n throw new Error('CAP_VITE_CAP_ARGS_JSON must be a JSON string array.')\n }\n\n return parsed\n}\n\nfunction parseConfigLoader(): 'bundle' | 'native' | 'runner' | undefined {\n const value = process.env.CAP_VITE_CONFIG_LOADER\n if (value === 'bundle' || value === 'native' || value === 'runner') {\n return value\n }\n\n return undefined\n}\n\nexport default defineConfig(async (env) => {\n const root = process.env.CAP_VITE_ROOT ?? process.cwd()\n const baseConfigFile = process.env.CAP_VITE_BASE_CONFIG || undefined\n const configLoader = parseConfigLoader()\n\n const loaded = await loadConfigFromFile(\n env,\n baseConfigFile,\n root,\n undefined,\n undefined,\n configLoader,\n )\n\n return mergeConfig(loaded?.config ?? {}, {\n plugins: [\n capVitePlugin({\n capArgs: parseCapArgs(),\n }),\n ],\n })\n})\n"],"mappings":";;;;AAMA,SAAS,eAAyB;CAChC,MAAM,QAAQ,QAAQ,IAAI;AAC1B,KAAI,CAAC,MACH,QAAO,EAAE;CAGX,MAAM,SAAS,KAAK,MAAM,MAAM;AAChC,KAAI,CAAC,MAAM,QAAQ,OAAO,IAAI,OAAO,MAAK,QAAO,OAAO,QAAQ,SAAS,CACvE,OAAM,IAAI,MAAM,sDAAsD;AAGxE,QAAO;;AAGT,SAAS,oBAAgE;CACvE,MAAM,QAAQ,QAAQ,IAAI;AAC1B,KAAI,UAAU,YAAY,UAAU,YAAY,UAAU,SACxD,QAAO;;AAMX,IAAA,8BAAe,aAAa,OAAO,QAAQ;CACzC,MAAM,OAAO,QAAQ,IAAI,iBAAiB,QAAQ,KAAK;AAavD,QAAO,aATQ,MAAM,mBACnB,KAJqB,QAAQ,IAAI,wBAAwB,KAAA,GAMzD,MACA,KAAA,GACA,KAAA,GAPmB,mBAAmB,CASvC,GAE0B,UAAU,EAAE,EAAE,EACvC,SAAS,CACP,cAAc,EACZ,SAAS,cAAc,EACxB,CAAC,CACH,EACF,CAAC;EACF"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@proj-airi/cap-vite",
3
3
  "type": "module",
4
- "version": "0.9.0-alpha.9",
4
+ "version": "0.9.0-beta.2",
5
5
  "description": "CLI that starts a Vite dev server and runs Capacitor live reload",
6
6
  "author": {
7
7
  "name": "Moeru AI Project AIRI Team",
@@ -35,8 +35,8 @@
35
35
  "vite": "^7.0.0 || ^8.0.0-beta.0"
36
36
  },
37
37
  "dependencies": {
38
- "cac": "^6.7.14",
39
- "tinyexec": "^1.0.2"
38
+ "cac": "^7.0.0",
39
+ "tinyexec": "^1.0.4"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsdown",