codeplay-common 4.10.1 → 4.10.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.10.3 — 2026-10-23
4
+
5
+ Version name: 4.10.3. Version code/build number: not applicable to this npm package.
6
+
7
+ Files changed: `package.json`, `files/finalrelease`, `CHANGELOG.md`, `RELEASE_NOTES.md`.
8
+
9
+ - Allow ad-free apps with `plugins.AdMob.ADMOB_ENABLED: false` to pass release validation without an AdMob alias or controller.
10
+ - Continue requiring a production AdMob alias when advertising is enabled or the enabled flag is unspecified.
11
+ - Preserve rejection of test aliases, commented or duplicate aliases, and missing production controllers for apps with ads enabled.
12
+
3
13
  ## 4.10.0 — 2026-10-23
4
14
 
5
15
  Version name: 4.10.0. Version code/build number: not applicable to this npm package.
package/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Release notes
2
2
 
3
+ ## 4.10.3 — 2026-10-23
4
+
5
+ Version name: 4.10.3. Version code/build number: not applicable to this npm package.
6
+
7
+ Files changed: `package.json`, `files/finalrelease`, `CHANGELOG.md`, `RELEASE_NOTES.md`.
8
+
9
+ - Release ad-free apps without adding an unnecessary AdMob alias or controller.
10
+ - Explicitly set `plugins.AdMob.ADMOB_ENABLED` to `false` to use the ad-free release path.
11
+ - Keep production AdMob safety checks for apps with advertising enabled.
12
+
3
13
  ## 4.10.0 — 2026-10-23
4
14
 
5
15
  Version name: 4.10.0. Version code/build number: not applicable to this npm package.
@@ -5,12 +5,137 @@ const fs = require('fs');
5
5
  const path = require('path');
6
6
  const plist = require('plist');
7
7
 
8
- // Preserve comments, strings, and custom tree-shaking objects/functions.
9
- // Only migrate the explicit boolean opt-out used by older Codeplay configs.
10
- const migrateViteTreeShaking = (content) => content.replace(
11
- /\/\*[\s\S]*?\*\/|\/\/[^\r\n]*|"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|`(?:\\[\s\S]|[^`\\])*`|\btreeshake([ \t]*:[ \t]*)false\b/g,
12
- (match, separator) => separator === undefined ? match : `treeshake${separator}true`
13
- );
8
+ // Edit only build.rollupOptions.treeshake in the exported Vite config.
9
+ // Keep source formatting/comments and valid custom option objects intact.
10
+ const migrateViteTreeShaking = (content) => {
11
+ const newline = content.includes('\r\n') ? '\r\n' : '\n';
12
+ const tokens = [];
13
+ const pairs = new Map();
14
+ const stack = [];
15
+ const lexer = /\s+|\/\*[\s\S]*?\*\/|\/\/[^\r\n]*|"(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|`(?:\\[\s\S]|[^`\\])*`|=>|\.\.\.|[\w$]+|[^\s]/gy;
16
+ let match;
17
+ while ((match = lexer.exec(content))) {
18
+ const value = match[0];
19
+ if (/^\s|^\/\*|^\/\//.test(value)) continue;
20
+ // Regex literals may contain braces/commas; treat them as a single value.
21
+ if (value === '/' && (!tokens.length || /^(?:=|\(|\[|:|,|return|=>|!|\?|\||&)$/.test(tokens.at(-1).value))) {
22
+ const regex = /^\/(?:\\[^\r\n]|\[(?:\\[^\r\n]|[^\]\\\r\n])*\]|[^/\\[\r\n])+\/[a-z]*/.exec(content.slice(match.index));
23
+ if (!regex) throw new Error('Cannot safely read Vite regex literal; configuration preserved.');
24
+ lexer.lastIndex = match.index + regex[0].length;
25
+ tokens.push({ value: regex[0], start: match.index, end: lexer.lastIndex });
26
+ continue;
27
+ }
28
+ const index = tokens.length;
29
+ tokens.push({ value, start: match.index, end: lexer.lastIndex });
30
+ if (['{', '[', '('].includes(value)) stack.push(index);
31
+ else if (['}', ']', ')'].includes(value)) {
32
+ const open = stack.pop();
33
+ if (open === undefined || tokens[open].value !== ({ '}': '{', ']': '[', ')': '(' })[value]) {
34
+ throw new Error('Cannot safely read Vite configuration; file preserved.');
35
+ }
36
+ pairs.set(open, index);
37
+ }
38
+ }
39
+ if (stack.length) throw new Error('Unbalanced Vite configuration; file preserved.');
40
+ const value = index => tokens[index]?.value;
41
+ const edits = [];
42
+ const properties = open => {
43
+ const result = [];
44
+ const close = pairs.get(open);
45
+ for (let i = open + 1; i < close;) {
46
+ if (value(i) === ',') { i++; continue; }
47
+ const start = i;
48
+ let key = i;
49
+ if (value(i) === '[') {
50
+ if (pairs.get(i) !== i + 2 || !/^['"]/.test(value(i + 1))) throw new Error('Dynamic Vite property name; file preserved.');
51
+ key = i + 1;
52
+ }
53
+ const accessor = ['get', 'set'].includes(value(i)) && value(i + 2) === '(';
54
+ if (accessor) key = i + 1;
55
+ const name = value(key).replace(/^(['"])(.*)\1$/, '$2');
56
+ const afterKey = value(start) === '[' ? pairs.get(start) + 1 : key + 1;
57
+ const colon = value(afterKey) === ':';
58
+ while (i < close && value(i) !== ',') i = pairs.has(i) ? pairs.get(i) + 1 : i + 1;
59
+ if (colon || (key === start && i === start + 1) || value(afterKey) === '(') {
60
+ result.push({ name, start, first: colon ? afterKey + 1 : start, end: i, comma: value(i) === ',', colon });
61
+ }
62
+ }
63
+ return result;
64
+ };
65
+ const findConfig = (index, visited = new Set()) => {
66
+ if (visited.has(index)) throw new Error('Circular Vite configuration reference; file preserved.');
67
+ visited.add(index);
68
+ if (value(index) === 'async') return findConfig(index + 1, visited);
69
+ if (value(index) === '(') {
70
+ const close = pairs.get(index);
71
+ if (value(close + 1) === '=>') return findBody(close + 2, visited);
72
+ return findConfig(index + 1, visited);
73
+ }
74
+ if (value(index + 1) === '=>') return findBody(index + 2, visited);
75
+ if (value(index) === 'function') {
76
+ let body = index + 1;
77
+ while (body < tokens.length && value(body) !== '{') body++;
78
+ return findBody(body, visited);
79
+ }
80
+ if (value(index) === '{') return index;
81
+ if (value(index + 1) === '(') return findConfig(index + 2, visited);
82
+ const declaration = tokens.findIndex((token, i) => ['const', 'let', 'var'].includes(token.value) && value(i + 1) === value(index) && value(i + 2) === '=');
83
+ if (declaration >= 0) return findConfig(declaration + 3, visited);
84
+ throw new Error('Unsupported dynamic Vite config; cannot safely set treeshake. File preserved.');
85
+ };
86
+ const findBody = (index, visited) => {
87
+ if (value(index) !== '{') return findConfig(index, visited);
88
+ const returns = [];
89
+ for (let i = index + 1; i < pairs.get(index); i++) {
90
+ if (value(i) === 'return') returns.push(i + 1);
91
+ if (pairs.has(i)) i = pairs.get(i);
92
+ }
93
+ if (returns.length !== 1) throw new Error('Vite config must return one static object for automatic treeshake migration. File preserved.');
94
+ return findConfig(returns[0], visited);
95
+ };
96
+ const append = (open, property) => {
97
+ const close = pairs.get(open);
98
+ const last = tokens[close - 1];
99
+ const line = content.slice(content.lastIndexOf('\n', tokens[open].start) + 1, tokens[open].start);
100
+ const indent = line.match(/^[ \t]*/)[0];
101
+ const needsComma = close > open + 1 && last.value !== ',';
102
+ const inlineComma = needsComma && last.end === tokens[close].start;
103
+ if (needsComma && !inlineComma) edits.push({ start: last.end, end: last.end, text: ',' });
104
+ edits.push({ start: tokens[close].start, end: tokens[close].start, text: `${inlineComma ? ',' : ''}${newline}${indent} ${property},${newline}${indent}` });
105
+ };
106
+ let exported = tokens.findIndex((token, i) => token.value === 'export' && value(i + 1) === 'default');
107
+ if (exported >= 0) exported += 2;
108
+ else {
109
+ exported = tokens.findIndex((token, i) => token.value === 'module' && value(i + 1) === '.' && value(i + 2) === 'exports' && value(i + 3) === '=');
110
+ if (exported >= 0) exported += 4;
111
+ }
112
+ if (exported < 0) throw new Error('No supported Vite config export found; file preserved.');
113
+ const config = findConfig(exported);
114
+ const build = properties(config).filter(property => property.name === 'build').at(-1);
115
+ if (!build) append(config, 'build: { rollupOptions: { treeshake: true } }');
116
+ else {
117
+ if (value(build.first) !== '{' || pairs.get(build.first) !== build.end - 1) throw new Error('Dynamic Vite build settings; file preserved.');
118
+ const rollup = properties(build.first).filter(property => property.name === 'rollupOptions').at(-1);
119
+ if (!rollup) append(build.first, 'rollupOptions: { treeshake: true }');
120
+ else {
121
+ if (value(rollup.first) !== '{' || pairs.get(rollup.first) !== rollup.end - 1) throw new Error('Dynamic Vite rollupOptions; file preserved.');
122
+ const entries = properties(rollup.first).filter(property => property.name === 'treeshake');
123
+ if (!entries.length) append(rollup.first, 'treeshake: true');
124
+ else {
125
+ // JavaScript uses the last duplicate; retain that setting and remove earlier ones.
126
+ const keep = entries.at(-1);
127
+ for (const entry of entries.slice(0, -1)) {
128
+ edits.push({ start: tokens[entry.start].start, end: entry.comma ? tokens[entry.end].end : tokens[entry.end - 1].end, text: '' });
129
+ }
130
+ const isTrue = value(keep.first) === 'true' && keep.end === keep.first + 1;
131
+ const isObject = value(keep.first) === '{' && pairs.get(keep.first) === keep.end - 1;
132
+ if (!isTrue && !isObject) edits.push({ start: tokens[keep.first].start, end: tokens[keep.end - 1].end, text: keep.colon ? 'true' : 'treeshake: true' });
133
+ }
134
+ }
135
+ }
136
+ for (const edit of edits.sort((a, b) => b.start - a.start)) content = content.slice(0, edit.start) + edit.text + content.slice(edit.end);
137
+ return content;
138
+ };
14
139
 
15
140
 
16
141
 
@@ -2952,7 +3077,7 @@ const syncViteTreeShaking = () => {
2952
3077
  const next = migrateViteTreeShaking(current);
2953
3078
  if (next !== current) {
2954
3079
  fs.writeFileSync(configPath, next, 'utf8');
2955
- console.log('✅ Enabled Vite tree shaking for common JavaScript and other unused exports.');
3080
+ console.log('✅ Vite treeshake setting updated without duplicate entries.');
2956
3081
  }
2957
3082
  };
2958
3083
 
@@ -90,6 +90,12 @@ function stripJavaScriptComments(content) {
90
90
  }
91
91
 
92
92
  function checkProductionAdmobAlias() {
93
+ const capacitorConfig = JSON.parse(readFileSync(resolve(__dirname, 'capacitor.config.json'), 'utf8'));
94
+ if (capacitorConfig.plugins?.AdMob?.ADMOB_ENABLED === false) {
95
+ console.log('ℹ️ AdMob is disabled; production @admob alias validation skipped.');
96
+ return;
97
+ }
98
+
93
99
  const possibleConfigFiles = ['vite.config.mjs', 'vite.config.js'];
94
100
  const viteConfigPath = possibleConfigFiles
95
101
  .map(configFile => resolve(__dirname, configFile))
@@ -133,7 +139,7 @@ function checkProductionAdmobAlias() {
133
139
  console.log(`✅ Production @admob alias verified: ${activeAlias}`);
134
140
  }
135
141
 
136
- // This must be the first release validation so test/no-ad builds cannot proceed.
142
+ // Validate the production alias first when AdMob is enabled.
137
143
  checkProductionAdmobAlias();
138
144
 
139
145
  // Define a mapping between store IDs and store names
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeplay-common",
3
- "version": "4.10.1",
3
+ "version": "4.10.3",
4
4
  "description": "Common build scripts and files",
5
5
  "scripts": {
6
6
  "postinstall": "node scripts/sync-files.js",