@choochmeque/tauri-windows-bundle 0.1.12 → 0.1.14

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
@@ -73,6 +73,8 @@ Edit `src-tauri/gen/windows/bundle.config.json`:
73
73
  }
74
74
  ```
75
75
 
76
+ `publisher` and `publisherDisplayName` are optional in `bundle.config.json`. If omitted, `publisher` falls back to `bundle.publisher` from `tauri.conf.json` or `tauri.windows.conf.json`. If `publisherDisplayName` is omitted, it defaults to the resolved `publisher` value.
77
+
76
78
  **Capabilities** are validated at build time. Three types are supported:
77
79
 
78
80
  ```json
@@ -91,14 +93,17 @@ Edit `src-tauri/gen/windows/bundle.config.json`:
91
93
 
92
94
  Note: `runFullTrust` is always auto-added (required for Tauri apps).
93
95
 
94
- **Auto-read from tauri.conf.json:**
96
+ **Auto-read from tauri.conf.json / tauri.windows.conf.json:**
95
97
  - `displayName` ← `productName`
96
98
  - `version` ← `version` (auto-converted to 4-part: `1.0.0` → `1.0.0.0`)
97
99
  - `description` ← `bundle.shortDescription`
100
+ - `publisher` ← `bundle.publisher` (fallback when not in bundle.config.json)
98
101
  - `icons` ← `bundle.icon`
99
102
  - `resources` ← `bundle.resources`
100
103
  - `signing` ← `bundle.windows.certificateThumbprint`
101
104
 
105
+ **Platform-specific config:** Values in `tauri.windows.conf.json` override `tauri.conf.json` using [JSON Merge Patch (RFC 7396)](https://datatracker.ietf.org/doc/html/rfc7396). This lets you define Windows-specific settings like `identifier`, `productName`, or `bundle.publisher` separately.
106
+
102
107
  ### Build
103
108
 
104
109
  ```bash
package/dist/cli.js CHANGED
@@ -43,6 +43,19 @@ function readTauriConfig(projectRoot) {
43
43
  throw new Error(`Failed to parse tauri.conf.json: ${error instanceof Error ? error.message : error}`);
44
44
  }
45
45
  }
46
+ function readTauriWindowsConfig(projectRoot) {
47
+ const configPath = path.join(projectRoot, 'src-tauri', 'tauri.windows.conf.json');
48
+ if (!fs.existsSync(configPath)) {
49
+ return null;
50
+ }
51
+ try {
52
+ const content = fs.readFileSync(configPath, 'utf-8');
53
+ return JSON.parse(content);
54
+ }
55
+ catch (error) {
56
+ throw new Error(`Failed to parse tauri.windows.conf.json: ${error instanceof Error ? error.message : error}`);
57
+ }
58
+ }
46
59
  function readBundleConfig$1(windowsDir) {
47
60
  const configPath = path.join(windowsDir, 'bundle.config.json');
48
61
  if (!fs.existsSync(configPath)) {
@@ -59,6 +72,26 @@ function readBundleConfig$1(windowsDir) {
59
72
  function getWindowsDir(projectRoot) {
60
73
  return path.join(projectRoot, 'src-tauri', 'gen', 'windows');
61
74
  }
75
+ function resolveVersion(version, tauriConfigDir) {
76
+ const resolvedPath = path.resolve(tauriConfigDir, version);
77
+ if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) {
78
+ try {
79
+ const content = fs.readFileSync(resolvedPath, 'utf-8');
80
+ const json = JSON.parse(content);
81
+ if (!json.version || typeof json.version !== 'string') {
82
+ throw new Error(`File ${version} does not contain a valid "version" field`);
83
+ }
84
+ return json.version;
85
+ }
86
+ catch (error) {
87
+ if (error instanceof SyntaxError) {
88
+ throw new Error(`Failed to parse ${version} as JSON: ${error.message}`);
89
+ }
90
+ throw error;
91
+ }
92
+ }
93
+ return version;
94
+ }
62
95
  function toFourPartVersion(version) {
63
96
  const parts = version.split('.');
64
97
  while (parts.length < 4)
@@ -382,7 +415,7 @@ function generateManifestTemplate(windowsDir) {
382
415
  }
383
416
  function generateManifest(config, arch, minVersion) {
384
417
  const variables = {
385
- PACKAGE_NAME: config.identifier.replace(/\./g, ''),
418
+ PACKAGE_NAME: config.identifier,
386
419
  PUBLISHER: config.publisher,
387
420
  VERSION: config.version,
388
421
  ARCH: arch,
@@ -654,6 +687,25 @@ function updatePackageJson(projectRoot) {
654
687
  }
655
688
  }
656
689
 
690
+ function isObject(val) {
691
+ return val !== null && typeof val === 'object' && !Array.isArray(val);
692
+ }
693
+ function jsonMergePatch(target, patch) {
694
+ if (!isObject(patch)) {
695
+ return patch;
696
+ }
697
+ const result = isObject(target) ? { ...target } : {};
698
+ for (const [key, value] of Object.entries(patch)) {
699
+ if (value === null) {
700
+ delete result[key];
701
+ }
702
+ else {
703
+ result[key] = jsonMergePatch(result[key], value);
704
+ }
705
+ }
706
+ return result;
707
+ }
708
+
657
709
  function prepareAppxContent(projectRoot, arch, config, tauriConfig, minVersion) {
658
710
  const target = arch === 'x64' ? 'x86_64-pc-windows-msvc' : 'aarch64-pc-windows-msvc';
659
711
  const srcTauriDir = path.join(projectRoot, 'src-tauri');
@@ -890,7 +942,11 @@ async function build(options) {
890
942
  const projectRoot = findProjectRoot();
891
943
  const windowsDir = getWindowsDir(projectRoot);
892
944
  // Read configs
893
- const tauriConfig = readTauriConfig(projectRoot);
945
+ let tauriConfig = readTauriConfig(projectRoot);
946
+ const windowsConfig = readTauriWindowsConfig(projectRoot);
947
+ if (windowsConfig) {
948
+ tauriConfig = jsonMergePatch(tauriConfig, windowsConfig);
949
+ }
894
950
  const bundleConfig = readBundleConfig$1(windowsDir);
895
951
  // Validate capabilities
896
952
  if (bundleConfig.capabilities) {
@@ -903,13 +959,22 @@ async function build(options) {
903
959
  process.exit(1);
904
960
  }
905
961
  }
962
+ // Resolve publisher with fallback to tauriConfig
963
+ const publisher = bundleConfig.publisher || tauriConfig.bundle?.publisher;
964
+ if (!publisher) {
965
+ console.error('Publisher is required. Set it in bundle.config.json or in tauri.conf.json / tauri.windows.conf.json under bundle.publisher');
966
+ process.exit(1);
967
+ }
968
+ const publisherDisplayName = bundleConfig.publisherDisplayName || publisher;
906
969
  // Merge config
907
970
  const config = {
908
971
  displayName: tauriConfig.productName || 'App',
909
- version: toFourPartVersion(tauriConfig.version || '1.0.0'),
972
+ version: toFourPartVersion(resolveVersion(tauriConfig.version || '1.0.0', path.join(projectRoot, 'src-tauri'))),
910
973
  description: tauriConfig.bundle?.shortDescription || '',
911
974
  identifier: tauriConfig.identifier || 'com.example.app',
912
975
  ...bundleConfig,
976
+ publisher,
977
+ publisherDisplayName,
913
978
  };
914
979
  // Architectures from CLI flag
915
980
  const architectures = options.arch?.split(',') || ['x64'];
@@ -1,6 +1,8 @@
1
1
  import type { TauriConfig, BundleConfig } from '../types.js';
2
2
  export declare function findProjectRoot(startDir?: string): string;
3
3
  export declare function readTauriConfig(projectRoot: string): TauriConfig;
4
+ export declare function readTauriWindowsConfig(projectRoot: string): TauriConfig | null;
4
5
  export declare function readBundleConfig(windowsDir: string): BundleConfig;
5
6
  export declare function getWindowsDir(projectRoot: string): string;
7
+ export declare function resolveVersion(version: string, tauriConfigDir: string): string;
6
8
  export declare function toFourPartVersion(version: string): string;
package/dist/index.js CHANGED
@@ -124,6 +124,19 @@ function readTauriConfig(projectRoot) {
124
124
  throw new Error(`Failed to parse tauri.conf.json: ${error instanceof Error ? error.message : error}`);
125
125
  }
126
126
  }
127
+ function readTauriWindowsConfig(projectRoot) {
128
+ const configPath = path.join(projectRoot, 'src-tauri', 'tauri.windows.conf.json');
129
+ if (!fs.existsSync(configPath)) {
130
+ return null;
131
+ }
132
+ try {
133
+ const content = fs.readFileSync(configPath, 'utf-8');
134
+ return JSON.parse(content);
135
+ }
136
+ catch (error) {
137
+ throw new Error(`Failed to parse tauri.windows.conf.json: ${error instanceof Error ? error.message : error}`);
138
+ }
139
+ }
127
140
  function readBundleConfig$1(windowsDir) {
128
141
  const configPath = path.join(windowsDir, 'bundle.config.json');
129
142
  if (!fs.existsSync(configPath)) {
@@ -140,6 +153,26 @@ function readBundleConfig$1(windowsDir) {
140
153
  function getWindowsDir(projectRoot) {
141
154
  return path.join(projectRoot, 'src-tauri', 'gen', 'windows');
142
155
  }
156
+ function resolveVersion(version, tauriConfigDir) {
157
+ const resolvedPath = path.resolve(tauriConfigDir, version);
158
+ if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) {
159
+ try {
160
+ const content = fs.readFileSync(resolvedPath, 'utf-8');
161
+ const json = JSON.parse(content);
162
+ if (!json.version || typeof json.version !== 'string') {
163
+ throw new Error(`File ${version} does not contain a valid "version" field`);
164
+ }
165
+ return json.version;
166
+ }
167
+ catch (error) {
168
+ if (error instanceof SyntaxError) {
169
+ throw new Error(`Failed to parse ${version} as JSON: ${error.message}`);
170
+ }
171
+ throw error;
172
+ }
173
+ }
174
+ return version;
175
+ }
143
176
  function toFourPartVersion(version) {
144
177
  const parts = version.split('.');
145
178
  while (parts.length < 4)
@@ -379,7 +412,7 @@ function generateManifestTemplate(windowsDir) {
379
412
  }
380
413
  function generateManifest(config, arch, minVersion) {
381
414
  const variables = {
382
- PACKAGE_NAME: config.identifier.replace(/\./g, ''),
415
+ PACKAGE_NAME: config.identifier,
383
416
  PUBLISHER: config.publisher,
384
417
  VERSION: config.version,
385
418
  ARCH: arch,
@@ -651,6 +684,25 @@ function updatePackageJson(projectRoot) {
651
684
  }
652
685
  }
653
686
 
687
+ function isObject(val) {
688
+ return val !== null && typeof val === 'object' && !Array.isArray(val);
689
+ }
690
+ function jsonMergePatch(target, patch) {
691
+ if (!isObject(patch)) {
692
+ return patch;
693
+ }
694
+ const result = isObject(target) ? { ...target } : {};
695
+ for (const [key, value] of Object.entries(patch)) {
696
+ if (value === null) {
697
+ delete result[key];
698
+ }
699
+ else {
700
+ result[key] = jsonMergePatch(result[key], value);
701
+ }
702
+ }
703
+ return result;
704
+ }
705
+
654
706
  function prepareAppxContent(projectRoot, arch, config, tauriConfig, minVersion) {
655
707
  const target = arch === 'x64' ? 'x86_64-pc-windows-msvc' : 'aarch64-pc-windows-msvc';
656
708
  const srcTauriDir = path.join(projectRoot, 'src-tauri');
@@ -887,7 +939,11 @@ async function build(options) {
887
939
  const projectRoot = findProjectRoot();
888
940
  const windowsDir = getWindowsDir(projectRoot);
889
941
  // Read configs
890
- const tauriConfig = readTauriConfig(projectRoot);
942
+ let tauriConfig = readTauriConfig(projectRoot);
943
+ const windowsConfig = readTauriWindowsConfig(projectRoot);
944
+ if (windowsConfig) {
945
+ tauriConfig = jsonMergePatch(tauriConfig, windowsConfig);
946
+ }
891
947
  const bundleConfig = readBundleConfig$1(windowsDir);
892
948
  // Validate capabilities
893
949
  if (bundleConfig.capabilities) {
@@ -900,13 +956,22 @@ async function build(options) {
900
956
  process.exit(1);
901
957
  }
902
958
  }
959
+ // Resolve publisher with fallback to tauriConfig
960
+ const publisher = bundleConfig.publisher || tauriConfig.bundle?.publisher;
961
+ if (!publisher) {
962
+ console.error('Publisher is required. Set it in bundle.config.json or in tauri.conf.json / tauri.windows.conf.json under bundle.publisher');
963
+ process.exit(1);
964
+ }
965
+ const publisherDisplayName = bundleConfig.publisherDisplayName || publisher;
903
966
  // Merge config
904
967
  const config = {
905
968
  displayName: tauriConfig.productName || 'App',
906
- version: toFourPartVersion(tauriConfig.version || '1.0.0'),
969
+ version: toFourPartVersion(resolveVersion(tauriConfig.version || '1.0.0', path.join(projectRoot, 'src-tauri'))),
907
970
  description: tauriConfig.bundle?.shortDescription || '',
908
971
  identifier: tauriConfig.identifier || 'com.example.app',
909
972
  ...bundleConfig,
973
+ publisher,
974
+ publisherDisplayName,
910
975
  };
911
976
  // Architectures from CLI flag
912
977
  const architectures = options.arch?.split(',') || ['x64'];
@@ -1687,4 +1752,4 @@ async function extensionRemove(type, name, options) {
1687
1752
  }
1688
1753
  }
1689
1754
 
1690
- export { DEFAULT_CAPABILITIES, DEFAULT_MIN_WINDOWS_VERSION, DEFAULT_RUNNER, DEVICE_CAPABILITIES, GENERAL_CAPABILITIES, MSIX_ASSETS, RESTRICTED_CAPABILITIES, build, extensionAddAppExecutionAlias, extensionAddAppService, extensionAddAutoplay, extensionAddBackgroundTask, extensionAddContextMenu, extensionAddFileAssociation, extensionAddPreviewHandler, extensionAddProtocol, extensionAddThumbnailHandler, extensionDisablePrintTaskSettings, extensionDisableShareTarget, extensionDisableStartupTask, extensionDisableToastActivation, extensionEnablePrintTaskSettings, extensionEnableShareTarget, extensionEnableStartupTask, extensionEnableToastActivation, extensionList, extensionRemove, findProjectRoot, generateManifest, generateManifestTemplate, getPackageVersion, getWindowsDir, init, prepareAppxContent, readBundleConfig$1 as readBundleConfig, readTauriConfig, toFourPartVersion, validateCapabilities };
1755
+ export { DEFAULT_CAPABILITIES, DEFAULT_MIN_WINDOWS_VERSION, DEFAULT_RUNNER, DEVICE_CAPABILITIES, GENERAL_CAPABILITIES, MSIX_ASSETS, RESTRICTED_CAPABILITIES, build, extensionAddAppExecutionAlias, extensionAddAppService, extensionAddAutoplay, extensionAddBackgroundTask, extensionAddContextMenu, extensionAddFileAssociation, extensionAddPreviewHandler, extensionAddProtocol, extensionAddThumbnailHandler, extensionDisablePrintTaskSettings, extensionDisableShareTarget, extensionDisableStartupTask, extensionDisableToastActivation, extensionEnablePrintTaskSettings, extensionEnableShareTarget, extensionEnableStartupTask, extensionEnableToastActivation, extensionList, extensionRemove, findProjectRoot, generateManifest, generateManifestTemplate, getPackageVersion, getWindowsDir, init, prepareAppxContent, readBundleConfig$1 as readBundleConfig, readTauriConfig, readTauriWindowsConfig, resolveVersion, toFourPartVersion, validateCapabilities };
package/dist/types.d.ts CHANGED
@@ -6,6 +6,7 @@ export interface TauriConfig {
6
6
  icon?: string[];
7
7
  shortDescription?: string;
8
8
  longDescription?: string;
9
+ publisher?: string;
9
10
  resources?: (string | {
10
11
  src: string;
11
12
  target: string;
@@ -21,8 +22,8 @@ export interface CapabilitiesConfig {
21
22
  restricted?: string[];
22
23
  }
23
24
  export interface BundleConfig {
24
- publisher: string;
25
- publisherDisplayName: string;
25
+ publisher?: string;
26
+ publisherDisplayName?: string;
26
27
  capabilities?: CapabilitiesConfig;
27
28
  extensions?: {
28
29
  shareTarget?: boolean;
@@ -95,6 +96,8 @@ export interface PreviewHandler {
95
96
  fileTypes: string[];
96
97
  }
97
98
  export interface MergedConfig extends BundleConfig {
99
+ publisher: string;
100
+ publisherDisplayName: string;
98
101
  displayName: string;
99
102
  version: string;
100
103
  description: string;
@@ -0,0 +1 @@
1
+ export declare function jsonMergePatch<T>(target: T, patch: unknown): T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@choochmeque/tauri-windows-bundle",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "MSIX packaging tool for Tauri apps - Windows Store ready bundles",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,14 +36,14 @@
36
36
  "@rollup/plugin-node-resolve": "^16.0.0",
37
37
  "@rollup/plugin-typescript": "^12.0.0",
38
38
  "@types/node": "^25.0.3",
39
- "@vitest/coverage-v8": "^2.0.0",
39
+ "@vitest/coverage-v8": "^4.0.0",
40
40
  "eslint": "^9.0.0",
41
41
  "prettier": "^3.0.0",
42
42
  "rollup": "^4.0.0",
43
43
  "tslib": "^2.6.0",
44
44
  "typescript": "^5.3.3",
45
45
  "typescript-eslint": "^8.0.0",
46
- "vitest": "^2.0.0"
46
+ "vitest": "^4.0.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "rollup -c",