@tamagui/build 2.0.0-rc.4 → 2.0.0-rc.40

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.
Files changed (27) hide show
  1. package/.turbo/turbo-test$colon$web.log +47 -0
  2. package/.turbo/turbo-test.log +22 -13
  3. package/README.md +4 -2
  4. package/__tests__/fixtures/js-main-package/.turbo/turbo-build.log +2 -0
  5. package/__tests__/fixtures/js-main-package/LICENSE +1 -0
  6. package/__tests__/fixtures/js-main-package/dist/cjs/index.js +24 -0
  7. package/__tests__/fixtures/js-main-package/dist/cjs/index.js.map +6 -0
  8. package/__tests__/fixtures/js-main-package/dist/cjs/index.native.js +31 -0
  9. package/__tests__/fixtures/js-main-package/dist/cjs/index.native.js.map +1 -0
  10. package/__tests__/fixtures/js-main-package/dist/esm/index.mjs +3 -0
  11. package/__tests__/fixtures/js-main-package/dist/esm/index.mjs.map +1 -0
  12. package/__tests__/fixtures/js-main-package/dist/esm/index.native.js +3 -0
  13. package/__tests__/fixtures/js-main-package/dist/esm/index.native.js.map +1 -0
  14. package/__tests__/fixtures/js-main-package/package.json +14 -0
  15. package/__tests__/fixtures/js-main-package/src/index.ts +1 -0
  16. package/__tests__/fixtures/js-main-package/tsconfig.json +13 -0
  17. package/__tests__/fixtures/js-main-package/types/index.d.ts +2 -0
  18. package/__tests__/fixtures/js-main-package/types/index.d.ts.map +1 -0
  19. package/__tests__/fixtures/simple-package/.turbo/turbo-build.log +3 -2
  20. package/__tests__/fixtures/simple-package/package.json +3 -1
  21. package/__tests__/fixtures/simple-package/src/index.ts +27 -0
  22. package/__tests__/fixtures/simple-package/src/nested/index.ts +1 -0
  23. package/__tests__/fixtures/watch-package/.turbo/turbo-build.log +1 -1
  24. package/__tests__/integration.test.ts +106 -3
  25. package/package.json +13 -6
  26. package/pretty-print-errors.js +33 -0
  27. package/tamagui-build.js +213 -57
@@ -0,0 +1,47 @@
1
+ $ bun run test
2
+ $ vitest --run
3
+
4
+ RUN v4.0.4 /Users/n8/tamagui/code/packages/build
5
+
6
+ $ node ../../../tamagui-build.js
7
+ $ node ../../../tamagui-build.js --bundle
8
+ $ node ../../../tamagui-build.js --skip-mjs
9
+ $ node ../../../tamagui-build.js --skip-sourcemaps
10
+ $ node ../../../tamagui-build.js --ignore-base-url
11
+ stdout | __tests__/integration.test.ts > tamagui-build integration test > should rebuild the package on file change when --watch is used
12
+ Watch process output: built tamagui-build-test-watch-package in 88 ms
13
+
14
+ Initial build complete, modifying file...
15
+
16
+ stdout | __tests__/integration.test.ts > tamagui-build integration test > should rebuild the package on file change when --watch is used
17
+ Watch process output: built tamagui-build-test-watch-package in 388 ms
18
+
19
+ Rebuild after file modification complete
20
+
21
+ $ node ../../../tamagui-build.js
22
+ $ node ../../../tamagui-build.js
23
+ $ node ../../../tamagui-build.js
24
+ $ MINIFY=true node ../../../tamagui-build.js
25
+ $ node ../../../tamagui-build.js
26
+ $ node ../../../tamagui-build.js
27
+ $ node ../../../tamagui-build.js
28
+ $ node ../../../tamagui-build.js
29
+ ✓ __tests__/integration.test.ts (12 tests) 11799ms
30
+ ✓ should build the package correctly 708ms
31
+ ✓ should bundle the package correctly 619ms
32
+ ✓ should skip mjs files when --skip-mjs is used 705ms
33
+ ✓ should skip sourcemaps when --skip-sourcemaps is used 687ms
34
+ ✓ should ignore base URL when --ignore-base-url is used 695ms
35
+ ✓ should rebuild the package on file change when --watch is used 806ms
36
+ ✓ should generate correct platform-specific output 690ms
37
+ ✓ should keep side-effectful native statements outside dev-only guards 696ms
38
+ ✓ should minify the output when MINIFY=true is set 1402ms
39
+ ✓ should clean stale outputs before building 2191ms
40
+ ✓ should keep only the required js aliases after postprocessing 1112ms
41
+ ✓ should keep explicit cjs .js mains as final output 1440ms
42
+
43
+ Test Files 1 passed (1)
44
+ Tests 12 passed (12)
45
+ Start at 18:32:32
46
+ Duration 11.92s (transform 44ms, setup 0ms, collect 51ms, tests 11.80s, environment 0ms, prepare 2ms)
47
+
@@ -5,31 +5,40 @@ $ vitest --run
5
5
  $ node ../../../tamagui-build.js
6
6
  $ node ../../../tamagui-build.js --bundle
7
7
  $ node ../../../tamagui-build.js --skip-mjs
8
+ $ node ../../../tamagui-build.js --skip-sourcemaps
8
9
  $ node ../../../tamagui-build.js --ignore-base-url
9
10
  stdout | __tests__/integration.test.ts > tamagui-build integration test > should rebuild the package on file change when --watch is used
10
- Watch process output: built tamagui-build-test-watch-package in 71 ms
11
+ Watch process output: built tamagui-build-test-watch-package in 80 ms
11
12
 
12
13
  Initial build complete, modifying file...
13
14
 
14
15
  stdout | __tests__/integration.test.ts > tamagui-build integration test > should rebuild the package on file change when --watch is used
15
- Watch process output: built tamagui-build-test-watch-package in 337 ms
16
+ Watch process output: built tamagui-build-test-watch-package in 361 ms
16
17
 
17
18
  Rebuild after file modification complete
18
19
 
19
20
  $ node ../../../tamagui-build.js
20
21
  $ node ../../../tamagui-build.js
21
22
  $ MINIFY=true node ../../../tamagui-build.js
22
- ✓ __tests__/integration.test.ts (7 tests) 5065ms
23
- ✓ should build the package correctly  589ms
24
- ✓ should bundle the package correctly  527ms
25
- ✓ should skip mjs files when --skip-mjs is used  604ms
26
- ✓ should ignore base URL when --ignore-base-url is used  599ms
27
- ✓ should rebuild the package on file change when --watch is used  883ms
28
- ✓ should generate correct platform-specific output  606ms
29
- ✓ should minify the output when MINIFY=true is set  1233ms
23
+ $ node ../../../tamagui-build.js
24
+ $ node ../../../tamagui-build.js
25
+ $ node ../../../tamagui-build.js
26
+ $ node ../../../tamagui-build.js
27
+ ✓ __tests__/integration.test.ts (11 tests) 8801ms
28
+ ✓ should build the package correctly  639ms
29
+ ✓ should bundle the package correctly  556ms
30
+ ✓ should skip mjs files when --skip-mjs is used  623ms
31
+ ✓ should skip sourcemaps when --skip-sourcemaps is used  638ms
32
+ ✓ should ignore base URL when --ignore-base-url is used  621ms
33
+ ✓ should rebuild the package on file change when --watch is used  765ms
34
+ ✓ should generate correct platform-specific output  629ms
35
+ ✓ should minify the output when MINIFY=true is set  1268ms
36
+ ✓ should clean stale outputs before building  1250ms
37
+ ✓ should keep only the required js aliases after postprocessing  621ms
38
+ ✓ should keep explicit cjs .js mains as final output  1149ms
30
39
 
31
40
   Test Files  1 passed (1)
32
-  Tests  7 passed (7)
33
-  Start at  08:53:06
34
-  Duration  5.18s (transform 37ms, setup 0ms, collect 43ms, tests 5.06s, environment 0ms, prepare 2ms)
41
+  Tests  11 passed (11)
42
+  Start at  17:43:04
43
+  Duration  8.91s (transform 38ms, setup 0ms, collect 45ms, tests 8.80s, environment 0ms, prepare 2ms)
35
44
 
package/README.md CHANGED
@@ -14,7 +14,7 @@ Some details on how it works:
14
14
  - in `.mjs`, adds path-specific imports to non-specific imports
15
15
  - outputs both `.js` and `.cjs` files in `dist/cjs`:
16
16
  - in `.cjs`, adds path-specific imports to non-specific imports
17
- - removes hanging imports that esbuild leaves (see `pkgRemoveSideEffects`)
17
+ - strips bare imports that esbuild leaves behind, respecting the `sideEffects` field in package.json
18
18
  - outputs `.native.js` and regular `.js` files for all output files, so React Native always loads separate files from web. In the `native` specific files,
19
19
  - swc is sued to transform to es5
20
20
  - `process.env.TAMAGUI_TARGET` is defined `native` (otherwise `web`)
@@ -30,7 +30,6 @@ It assumes your package.json looks something like this:
30
30
  "main": "dist/cjs",
31
31
  "module": "dist/esm",
32
32
  "type": "module",
33
- "removeSideEffects": "true",
34
33
  "scripts": {
35
34
  "build": "tamagui-build",
36
35
  "watch": "tamagui-build --watch",
@@ -69,12 +68,15 @@ It assumes your package.json looks something like this:
69
68
  ### Use
70
69
 
71
70
  - `tamagui-build` - builds `src` folder to `dist` and `types` folders
71
+ - normal builds clear `dist` and `types` first, so stale transformed files don't hang around
72
+ - intermediary `.js` files are removed after `.mjs` / `.cjs` postprocessing, so published output stays lean
72
73
  - `tamagui build .` second argument sets baseUrl to tsc
73
74
  - `--bundle-modules` - inline node_modules
74
75
  - `--declaration-root` - sets tsc flag `--declarationDir ./`
75
76
  - `--ignore-base-url` - if not set, tsc is passed `--baseUrl .`
76
77
  - `--skip-mjs` - don't output mjs files
77
78
  - `--skip-native` - don't output native files
79
+ - `--skip-sourcemaps` - don't output js or declaration sourcemaps
78
80
  - `--swap-exports` - swaps `exports.types` from `./src/*.ts` to `./types/*.d.ts` for publishing. if a command is given after `--`, runs it then swaps back. exit code is preserved.
79
81
  - `tamagui-build --swap-exports` - build and swap, stays swapped (for manual publish)
80
82
  - `tamagui-build --swap-exports -- npm publish` - build, swap, publish, swap back
@@ -0,0 +1,2 @@
1
+ $ node ../../../tamagui-build.js
2
+ built tamagui-build-test-js-main-package in 1390 ms
@@ -0,0 +1,24 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var index_exports = {};
19
+ __export(index_exports, {
20
+ jsMainFixture: () => jsMainFixture
21
+ });
22
+ module.exports = __toCommonJS(index_exports);
23
+ const jsMainFixture = "ok";
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index.ts"],
4
+ "mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,gBAAgB;",
5
+ "names": []
6
+ }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all) __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true
11
+ });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: () => from[key],
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
+ };
22
+ var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
23
+ value: true
24
+ }), mod);
25
+ var index_exports = {};
26
+ __export(index_exports, {
27
+ jsMainFixture: () => jsMainFixture
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var jsMainFixture = "ok";
31
+ //# sourceMappingURL=index.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["__toCommonJS","mod","__copyProps","__defProp","value","index_exports","__export","jsMainFixture","module","exports"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,YAAA,GAAAC,GAAA,IAAAC,WAAA,CAAAC,SAAA;EAAAC,KAAA;AAAA,IAAAH,GAAA;AAAA,IAAAI,aAAA;AAAAC,QAAA,CAAAD,aAAA;EAAAE,aAAA,EAAAA,CAAA,KAAAA;AAAA;AAAOC,MAAM,CAAAC,OAAA,GAAAT,YAAgB,CAAAK,aAAA","ignoreList":[]}
@@ -0,0 +1,3 @@
1
+ const jsMainFixture = "ok";
2
+ export { jsMainFixture };
3
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["jsMainFixture"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAO,MAAMA,aAAA,GAAgB","ignoreList":[]}
@@ -0,0 +1,3 @@
1
+ var jsMainFixture = "ok";
2
+ export { jsMainFixture };
3
+ //# sourceMappingURL=index.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["jsMainFixture"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAO,IAAAA,aAAM,OAAgB","ignoreList":[]}
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "tamagui-build-test-js-main-package",
3
+ "version": "2.0.0-rc.0-1769885482630",
4
+ "main": "dist/cjs/index.js",
5
+ "module": "dist/esm/index.mjs",
6
+ "types": "types/index.d.ts",
7
+ "scripts": {
8
+ "build": "node ../../../tamagui-build.js"
9
+ },
10
+ "devDependencies": {
11
+ "@tamagui/build": "workspace:*",
12
+ "typescript": "~5.9.2"
13
+ }
14
+ }
@@ -0,0 +1 @@
1
+ export const jsMainFixture = 'ok'
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "strict": true,
7
+ "declaration": true,
8
+ "jsx": "react-jsx",
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src"]
13
+ }
@@ -0,0 +1,2 @@
1
+ export declare const jsMainFixture = "ok";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,OAAO,CAAA"}
@@ -1,2 +1,3 @@
1
- $ node ../../../tamagui-build.js
2
- built tamagui-build-test-simple-tpackage in 1430 ms
1
+
2
+ $ node ../../../tamagui-build.js
3
+ built tamagui-build-test-simple-tpackage in 1466 ms
@@ -2,12 +2,14 @@
2
2
  "name": "tamagui-build-test-simple-tpackage",
3
3
  "version": "2.0.0-rc.0-1769885482630",
4
4
  "main": "dist/cjs",
5
- "module": "dist/esm/index.mjs",
5
+ "module": "dist/esm",
6
+ "module:jsx": "dist/jsx",
6
7
  "types": "dist/types/index.d.ts",
7
8
  "scripts": {
8
9
  "build": "node ../../../tamagui-build.js",
9
10
  "build:bundle": "node ../../../tamagui-build.js --bundle",
10
11
  "build:skip-mjs": "node ../../../tamagui-build.js --skip-mjs",
12
+ "build:skip-sourcemaps": "node ../../../tamagui-build.js --skip-sourcemaps",
11
13
  "build:declaration-root": "node ../../../tamagui-build.js --declaration-root",
12
14
  "build:ignore-base-url": "node ../../../tamagui-build.js --ignore-base-url",
13
15
  "build:watch": "node ../../../tamagui-build.js --watch",
@@ -1,3 +1,5 @@
1
+ export { nestedHello } from './nested'
2
+
1
3
  export const greet = (name: string): string => {
2
4
  return `Hello, ${name}!`
3
5
  }
@@ -8,3 +10,28 @@ export const paltformGreeter = (name: string): string => {
8
10
  process.env.TAMAGUI_TARGET === 'native' ? (salutation = 'Hey') : (salutation = 'Hello')
9
11
  return `${salutation}, ${name}!`
10
12
  }
13
+
14
+ export function runNativeSideEffect(items: string[]) {
15
+ items.push('ran')
16
+ }
17
+
18
+ export function guardNativeSideEffects(items: string[], debug?: string) {
19
+ runNativeSideEffect(items)
20
+
21
+ if (process.env.NODE_ENV === 'development' && debug === 'verbose') {
22
+ console.log(items.length)
23
+ }
24
+
25
+ return items
26
+ }
27
+
28
+ export function getPlatformMarker() {
29
+ return process.env.TAMAGUI_TARGET === 'native' ? 'native-only-marker' : 'web-only-marker'
30
+ }
31
+
32
+ export function applyNativeLogicalMarker(items: string[]) {
33
+ process.env.TAMAGUI_TARGET === 'native' && items.push('native-logical-marker')
34
+ process.env.TAMAGUI_TARGET !== 'native' && items.push('web-logical-marker')
35
+
36
+ return items
37
+ }
@@ -0,0 +1 @@
1
+ export const nestedHello = 'nested hello'
@@ -1,2 +1,2 @@
1
1
  $ node ../../../tamagui-build.js
2
- built tamagui-build-test-watch-package in 1660 ms
2
+ built tamagui-build-test-watch-package in 653 ms
@@ -1,5 +1,5 @@
1
1
  import { execSync, spawn } from 'node:child_process'
2
- import { existsSync, readFileSync, writeFileSync, statSync } from 'node:fs'
2
+ import { existsSync, readFileSync, writeFileSync, statSync, readdirSync } from 'node:fs'
3
3
  import { describe, it, expect, beforeAll, afterAll } from 'vitest'
4
4
  import { join } from 'node:path'
5
5
  import { readFile } from 'node:fs/promises'
@@ -15,6 +15,8 @@ const distCjsFilePath = join(distPath, 'cjs', 'index.cjs')
15
15
  const watchDistCjsFilePath = join(watchDistPath, 'cjs', 'watch.cjs')
16
16
  const distEsmFilePath = join(distPath, 'esm', 'index.mjs')
17
17
  const distTypesFilePath = join(simplePackagePath, 'types', 'index.d.ts')
18
+ const jsMainPackagePath = join(__dirname, 'fixtures', 'js-main-package')
19
+ const jsMainDistPath = join(jsMainPackagePath, 'dist')
18
20
  // console.log({
19
21
  // distCjsFilePath,
20
22
  // distEsmFilePath,
@@ -28,6 +30,7 @@ describe('tamagui-build integration test', () => {
28
30
  beforeAll(() => {
29
31
  // Clean up dist directory before starting
30
32
  execSync('rm -rf dist && rm -rf types', { cwd: simplePackagePath })
33
+ execSync('rm -rf dist && rm -rf types', { cwd: jsMainPackagePath })
31
34
  })
32
35
 
33
36
  it('should build the package correctly', () => {
@@ -43,6 +46,10 @@ describe('tamagui-build integration test', () => {
43
46
  const esmOutput = readFileSync(distEsmFilePath, 'utf-8')
44
47
  expect(cjsOutput).toContain('Hello,')
45
48
  expect(esmOutput).toContain('Hello,')
49
+ expect(esmOutput).toContain("./nested/index.mjs")
50
+ expect(existsSync(join(distPath, 'cjs', 'index.cjs'))).toBe(true)
51
+ expect(existsSync(join(distPath, 'esm', 'index.js'))).toBe(true)
52
+ expect(existsSync(join(distPath, 'jsx', 'index.js'))).toBe(true)
46
53
  })
47
54
 
48
55
  it('should bundle the package correctly', () => {
@@ -57,6 +64,9 @@ describe('tamagui-build integration test', () => {
57
64
  const esmOutput = readFileSync(distEsmFilePath, 'utf-8')
58
65
  expect(cjsOutput).toContain('Hello,')
59
66
  expect(esmOutput).toContain('Hello,')
67
+ expect(existsSync(join(distPath, 'cjs', 'index.cjs'))).toBe(true)
68
+ expect(existsSync(join(distPath, 'esm', 'index.js'))).toBe(true)
69
+ expect(existsSync(join(distPath, 'jsx', 'index.js'))).toBe(true)
60
70
  })
61
71
 
62
72
  it('should skip mjs files when --skip-mjs is used', () => {
@@ -66,6 +76,27 @@ describe('tamagui-build integration test', () => {
66
76
  // Check if the output files exist
67
77
  expect(existsSync(distCjsFilePath)).toBe(true)
68
78
  expect(existsSync(distEsmFilePath)).toBe(false)
79
+ expect(existsSync(join(distPath, 'cjs', 'index.cjs'))).toBe(true)
80
+ })
81
+
82
+ it('should skip sourcemaps when --skip-sourcemaps is used', () => {
83
+ execSync('rm -rf dist && rm -rf types', { cwd: simplePackagePath })
84
+ execSync('bun run build:skip-sourcemaps', { cwd: simplePackagePath })
85
+
86
+ expect(existsSync(distCjsFilePath)).toBe(true)
87
+ expect(existsSync(distEsmFilePath)).toBe(true)
88
+ expect(existsSync(distTypesFilePath)).toBe(true)
89
+ expect(existsSync(join(distPath, 'cjs', 'index.cjs.map'))).toBe(false)
90
+ expect(existsSync(join(distPath, 'esm', 'index.mjs.map'))).toBe(false)
91
+ expect(existsSync(join(simplePackagePath, 'types', 'index.d.ts.map'))).toBe(false)
92
+
93
+ const cjsOutput = readFileSync(distCjsFilePath, 'utf-8')
94
+ const esmOutput = readFileSync(distEsmFilePath, 'utf-8')
95
+ const typesOutput = readFileSync(distTypesFilePath, 'utf-8')
96
+
97
+ expect(cjsOutput).not.toContain('sourceMappingURL=')
98
+ expect(esmOutput).not.toContain('sourceMappingURL=')
99
+ expect(typesOutput).not.toContain('sourceMappingURL=')
69
100
  })
70
101
 
71
102
  it('should ignore base URL when --ignore-base-url is used', () => {
@@ -152,11 +183,31 @@ describe('tamagui-build integration test', () => {
152
183
  expect(nativeOutput).toContain('greet:')
153
184
  })
154
185
 
186
+ it('should keep side-effectful native statements outside dev-only guards', async () => {
187
+ execSync('bun run build', { cwd: simplePackagePath })
188
+
189
+ const nativeOutputPath = join(distPath, 'esm', 'index.native.js')
190
+ const nativeOutput = await readFile(nativeOutputPath, 'utf-8')
191
+
192
+ expect(nativeOutput).toContain('runNativeSideEffect(items);')
193
+ expect(nativeOutput).toContain('native-only-marker')
194
+ expect(nativeOutput).toContain('native-logical-marker')
195
+ expect(nativeOutput).not.toContain('if (runNativeSideEffect(')
196
+ expect(nativeOutput).not.toContain('if (false)')
197
+ expect(nativeOutput).not.toContain('if (true)')
198
+ expect(nativeOutput).not.toContain('runNativeSideEffect(items), process.env.NODE_ENV')
199
+ expect(nativeOutput).not.toContain('web-only-marker')
200
+ expect(nativeOutput).not.toContain('web-logical-marker')
201
+ expect(nativeOutput).not.toContain('&& items.push(')
202
+ })
203
+
155
204
  it('should minify the output when MINIFY=true is set', () => {
156
205
  // Build without minification and cache file sizes
157
206
  execSync('bun run build', { cwd: simplePackagePath })
158
207
  const originalCjsSize = statSync(distCjsFilePath).size
159
208
  const originalEsmSize = statSync(distEsmFilePath).size
209
+ const originalCjsOutput = readFileSync(distCjsFilePath, 'utf-8')
210
+ const originalEsmOutput = readFileSync(distEsmFilePath, 'utf-8')
160
211
 
161
212
  // Clean up the output
162
213
  execSync('rm -rf dist && rm -rf types', { cwd: simplePackagePath })
@@ -183,12 +234,64 @@ describe('tamagui-build integration test', () => {
183
234
  expect(esmOutput).not.toMatch(/^\s+$/m) // No lines with only whitespace
184
235
 
185
236
  // Check that the number of lines is reduced
186
- expect(cjsOutput.split('\n').length).toBeLessThan(32)
187
- expect(esmOutput.split('\n').length).toBeLessThan(32)
237
+ expect(cjsOutput.split('\n').length).toBeLessThanOrEqual(
238
+ originalCjsOutput.split('\n').length
239
+ )
240
+ expect(esmOutput.split('\n').length).toBeLessThanOrEqual(
241
+ originalEsmOutput.split('\n').length
242
+ )
243
+ })
244
+
245
+ it('should clean stale outputs before building', () => {
246
+ execSync('bun run build', { cwd: simplePackagePath })
247
+
248
+ const staleFilePath = join(distPath, 'esm', 'stale.mjs')
249
+ const staleTypesPath = join(simplePackagePath, 'types', 'stale.d.ts')
250
+ writeFileSync(staleFilePath, 'stale')
251
+ writeFileSync(staleTypesPath, 'stale')
252
+
253
+ expect(existsSync(staleFilePath)).toBe(true)
254
+ expect(existsSync(staleTypesPath)).toBe(true)
255
+
256
+ execSync('bun run build', { cwd: simplePackagePath })
257
+
258
+ expect(existsSync(staleFilePath)).toBe(false)
259
+ expect(existsSync(staleTypesPath)).toBe(false)
260
+ expect(readdirSync(join(distPath, 'esm'))).not.toContain('stale.mjs')
261
+ })
262
+
263
+ it('should keep only the required js aliases after postprocessing', () => {
264
+ execSync('bun run build', { cwd: simplePackagePath })
265
+
266
+ const distFiles = execSync('find dist -type f | sort', {
267
+ cwd: simplePackagePath,
268
+ encoding: 'utf-8',
269
+ })
270
+ .trim()
271
+ .split('\n')
272
+ .map((file) => file.replace(/^dist\//, ''))
273
+
274
+ expect(distFiles).toContain('cjs/index.cjs')
275
+ expect(distFiles).toContain('esm/index.js')
276
+ expect(distFiles).toContain('esm/index.js.map')
277
+ expect(distFiles).toContain('jsx/index.js')
278
+ expect(distFiles).toContain('jsx/index.js.map')
279
+ expect(distFiles).toContain('cjs/index.cjs')
280
+ expect(distFiles).toContain('esm/index.mjs')
281
+ expect(distFiles).toContain('esm/nested/index.mjs')
282
+ })
283
+
284
+ it('should keep explicit cjs .js mains as final output', () => {
285
+ execSync('bun run build', { cwd: jsMainPackagePath })
286
+
287
+ expect(existsSync(join(jsMainDistPath, 'cjs', 'index.js'))).toBe(true)
288
+ expect(existsSync(join(jsMainDistPath, 'cjs', 'index.mjs'))).toBe(false)
289
+ expect(existsSync(join(jsMainDistPath, 'esm', 'index.mjs'))).toBe(true)
188
290
  })
189
291
 
190
292
  afterAll(() => {
191
293
  // Clean up dist directory after tests
192
294
  execSync('rm -rf dist && rm -rf types', { cwd: simplePackagePath })
295
+ execSync('rm -rf dist && rm -rf types', { cwd: jsMainPackagePath })
193
296
  })
194
297
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tamagui/build",
3
- "version": "2.0.0-rc.4",
3
+ "version": "2.0.0-rc.40",
4
4
  "gitHead": "a49cc7ea6b93ba384e77a4880ae48ac4a5635c14",
5
5
  "bin": {
6
6
  "tamagui-build": "tamagui-build.js",
@@ -10,27 +10,34 @@
10
10
  "access": "public"
11
11
  },
12
12
  "scripts": {
13
- "test": "vitest --run"
13
+ "test": "vitest --run",
14
+ "test:web": "bun run test"
14
15
  },
15
16
  "dependencies": {
16
17
  "@babel/core": "^7.25.2",
17
18
  "@babel/preset-typescript": "^7.26.0",
18
19
  "@swc/core": "^1.14.0",
19
- "@tamagui/babel-plugin-fully-specified": "2.0.0-rc.4",
20
+ "@tamagui/babel-plugin-fully-specified": "2.0.0-rc.40",
20
21
  "@types/fs-extra": "^9.0.13",
21
- "babel-plugin-react-compiler": "^1.0.0",
22
22
  "baseline-browser-mapping": "^2.9.15",
23
23
  "chokidar": "^3.5.2",
24
24
  "esbuild": "^0.27.2",
25
- "esbuild-plugin-es5": "^2.1.1",
26
25
  "esbuild-register": "^3.6.0",
27
26
  "execa": "^5.0.0",
28
27
  "fast-glob": "^3.2.11",
29
28
  "fs-extra": "^11.2.0",
30
29
  "lodash.debounce": "^4.0.8",
31
- "oxc-transform": "^0.95.0",
30
+ "oxc-transform": "^0.112.0",
32
31
  "typescript": "~5.9.2"
33
32
  },
33
+ "peerDependencies": {
34
+ "babel-plugin-react-compiler": ">=1"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "babel-plugin-react-compiler": {
38
+ "optional": true
39
+ }
40
+ },
34
41
  "devDependencies": {
35
42
  "vitest": "4.0.4"
36
43
  }
@@ -125,9 +125,42 @@ function printTypescriptCompilationError(err, packageName) {
125
125
  console.error('')
126
126
  }
127
127
 
128
+ /**
129
+ * Pretty print OXC isolated declaration errors
130
+ * @param {Array<{ severity: string, message: string, labels: Array<{ message: string | null, start: number, end: number }>, helpMessage: string | null, codeframe: string | null }>} errors
131
+ */
132
+ function printOxcErrors(errors) {
133
+ console.error('\n❌ Type declaration generation errors:\n')
134
+
135
+ errors.forEach((error) => {
136
+ const severity = error.severity === 'Error' ? '❌' : '⚠️ '
137
+ console.error(` ${severity} ${error.message}`)
138
+
139
+ if (error.labels && error.labels.length > 0) {
140
+ error.labels.forEach((label) => {
141
+ console.error(` at ${label.start}-${label.end}`)
142
+ if (label.message) {
143
+ console.error(` ${label.message}`)
144
+ }
145
+ })
146
+ }
147
+
148
+ if (error.helpMessage) {
149
+ console.error(` help: ${error.helpMessage}`)
150
+ }
151
+
152
+ if (error.codeframe) {
153
+ console.error(`\n${error.codeframe}`)
154
+ }
155
+ })
156
+
157
+ console.error('')
158
+ }
159
+
128
160
  module.exports = {
129
161
  printTypescriptDiagnostics,
130
162
  printEsbuildError,
131
163
  printBuildError,
132
164
  printTypescriptCompilationError,
165
+ printOxcErrors,
133
166
  }
package/tamagui-build.js CHANGED
@@ -9,6 +9,7 @@
9
9
  * tamagui-build --watch # watch mode
10
10
  * tamagui-build --skip-types # js only
11
11
  * tamagui-build --skip-native # skip native builds
12
+ * tamagui-build --skip-sourcemaps # disable js + d.ts sourcemaps
12
13
  * tamagui-build clean # remove dist/types/node_modules
13
14
  * tamagui-build clean:build # remove dist/types only
14
15
  *
@@ -30,6 +31,7 @@ const createExternalPlugin = require('./externalNodePlugin')
30
31
  const debounce = require('lodash.debounce')
31
32
  const { basename, dirname } = require('node:path')
32
33
  const { es5Plugin } = require('./esbuild-es5')
34
+ const { transformSync: oxcTransformSync } = require('oxc-transform')
33
35
  const ts = require('typescript')
34
36
  const path = require('node:path')
35
37
  const childProcess = require('node:child_process')
@@ -38,6 +40,7 @@ const {
38
40
  printEsbuildError,
39
41
  printBuildError,
40
42
  printTypescriptCompilationError,
43
+ printOxcErrors,
41
44
  } = require('./pretty-print-errors')
42
45
 
43
46
  const jsOnly = !!process.env.JS_ONLY
@@ -45,12 +48,59 @@ const skipJS = !!(process.env.SKIP_JS || false)
45
48
 
46
49
  // write file only if contents changed to avoid triggering watchers
47
50
  async function writeIfUnchanged(filePath, contents) {
51
+ const isExecutableScript = typeof contents === 'string' && contents.startsWith('#!')
48
52
  const existing = await FSE.readFile(filePath, 'utf8').catch(() => null)
49
- if (existing === contents) return false
50
- await FSE.outputFile(filePath, contents, 'utf8')
53
+ if (existing === contents) {
54
+ if (isExecutableScript) {
55
+ await FSE.chmod(filePath, 0o755).catch(() => {})
56
+ }
57
+ return false
58
+ }
59
+ await FSE.outputFile(filePath, contents, {
60
+ encoding: 'utf8',
61
+ mode: isExecutableScript ? 0o755 : 0o666,
62
+ })
63
+ if (isExecutableScript) {
64
+ await FSE.chmod(filePath, 0o755).catch(() => {})
65
+ }
51
66
  return true
52
67
  }
53
68
 
69
+ function dceTamaguiTarget(contents, { format, jsx, platform }) {
70
+ if (!contents.includes('process.env.TAMAGUI_TARGET')) {
71
+ return contents
72
+ }
73
+
74
+ const result = oxcTransformSync(`tamagui-target.${jsx === 'preserve' ? 'jsx' : 'js'}`, contents, {
75
+ lang: jsx === 'preserve' ? 'jsx' : 'js',
76
+ jsx: jsx === 'preserve' ? 'preserve' : undefined,
77
+ sourceType: format === 'cjs' ? 'commonjs' : 'module',
78
+ define: {
79
+ 'process.env.TAMAGUI_TARGET': JSON.stringify(platform),
80
+ },
81
+ })
82
+
83
+ if (result.errors?.length) {
84
+ throw new Error(
85
+ `Failed to DCE TAMAGUI_TARGET for ${platform}: ${result.errors
86
+ .map((error) => error.message)
87
+ .join('\n')}`
88
+ )
89
+ }
90
+
91
+ return result.code || contents
92
+ }
93
+
94
+ function hasFlag(flag) {
95
+ return process.argv.includes(flag)
96
+ }
97
+
98
+ function getEnvFlag(name) {
99
+ const value = process.env[name]
100
+ if (!value) return false
101
+ return !['0', 'false', 'no', 'off'].includes(String(value).toLowerCase())
102
+ }
103
+
54
104
  /**
55
105
  * esbuild plugin that runs React Compiler on TS/TSX files before transformation
56
106
  */
@@ -105,32 +155,34 @@ const reactCompilerPlugin = {
105
155
  },
106
156
  }
107
157
 
108
- const shouldSwapExports = process.argv.includes('--swap-exports')
158
+ const shouldSwapExports = hasFlag('--swap-exports')
109
159
 
110
160
  // when swapping exports for publish, we MUST build types (ignore --skip-types)
111
161
  const shouldSkipTypes = shouldSwapExports
112
162
  ? false
113
- : !!(process.argv.includes('--skip-types') || process.env.SKIP_TYPES)
163
+ : !!(hasFlag('--skip-types') || process.env.SKIP_TYPES)
114
164
 
115
- const shouldSkipNative = !!process.argv.includes('--skip-native')
116
- const shouldSkipMJS = !!process.argv.includes('--skip-mjs')
165
+ const shouldSkipNative = hasFlag('--skip-native')
166
+ const shouldSkipMJS = hasFlag('--skip-mjs')
167
+ const shouldSkipSourceMaps =
168
+ hasFlag('--skip-sourcemaps') || getEnvFlag('TAMAGUI_BUILD_SKIP_SOURCEMAPS')
117
169
  // React Compiler is disabled by default - use --react-compiler to enable
118
170
  const shouldEnableCompiler = !!(
119
- process.argv.includes('--react-compiler') || process.env.REACT_COMPILER
171
+ hasFlag('--react-compiler') || process.env.REACT_COMPILER
120
172
  )
121
- const shouldBundleFlag = !!process.argv.includes('--bundle')
122
- const shouldBundleNodeModules = !!process.argv.includes('--bundle-modules')
173
+ const shouldBundleFlag = hasFlag('--bundle')
174
+ const shouldBundleNodeModules = hasFlag('--bundle-modules')
123
175
  const shouldClean = !!process.argv.includes('clean')
124
176
  const shouldCleanBuildOnly = !!process.argv.includes('clean:build')
125
- const shouldWatch = process.argv.includes('--watch')
177
+ const shouldWatch = hasFlag('--watch')
126
178
 
127
179
  // get command after "--" to run with swapped exports
128
180
  const dashDashIndex = process.argv.indexOf('--')
129
181
  const runCommandAfterSwap =
130
182
  dashDashIndex > -1 ? process.argv.slice(dashDashIndex + 1) : null
131
183
 
132
- const declarationToRoot = !!process.argv.includes('--declaration-root')
133
- const ignoreBaseUrl = process.argv.includes('--ignore-base-url')
184
+ const declarationToRoot = hasFlag('--declaration-root')
185
+ const ignoreBaseUrl = hasFlag('--ignore-base-url')
134
186
  const baseUrlIndex = process.argv.indexOf('--base-url')
135
187
  const tsProjectIndex = process.argv.indexOf('--ts-project')
136
188
  const exludeIndex = process.argv.indexOf('--exclude')
@@ -153,7 +205,6 @@ const pkgSource = pkg.source
153
205
  const pkgModule = pkg.module
154
206
  const pkgModuleJSX = pkg['module:jsx']
155
207
  const pkgTypes = Boolean(pkg.types || pkg.typings)
156
- const pkgRemoveSideEffects = pkg.removeSideEffects || false
157
208
 
158
209
  // build config from package.json
159
210
  const buildConfig = pkg['tamagui-build'] || {}
@@ -166,6 +217,20 @@ const bundleExternal = buildConfig.bundleExternal || null
166
217
  const flatOut = [pkgMain, pkgModule, pkgModuleJSX].filter(Boolean).length === 1
167
218
 
168
219
  const avoidCJS = pkgMain?.endsWith('.js')
220
+ const getJsEntryAliasPath = (entry) => {
221
+ if (!entry) return null
222
+ if (!path.extname(entry)) {
223
+ return path.join(entry, 'index.js').replace(/\\/g, '/')
224
+ }
225
+ if (entry.endsWith('.js')) {
226
+ return entry.replace(/\\/g, '/')
227
+ }
228
+ return null
229
+ }
230
+ const cjsMainAliasPath = getJsEntryAliasPath(pkgMain)
231
+ const esmAliasPaths = [getJsEntryAliasPath(pkgModule), getJsEntryAliasPath(pkgModuleJSX)].filter(
232
+ Boolean
233
+ )
169
234
 
170
235
  const replaceRNWeb = {
171
236
  esm: (content) =>
@@ -236,13 +301,14 @@ if (shouldClean || shouldCleanBuildOnly) {
236
301
  if (shouldWatch) {
237
302
  process.env.IS_WATCHING = true
238
303
  process.env.DISABLE_AUTORUN = true
239
- const rebuild = debounce(build, 100)
304
+ const rebuild = debounce(() => build({ cleanOutput: false }), 100)
240
305
  const chokidar = require('chokidar')
241
306
 
242
307
  if (!process.env.SKIP_INITIAL_BUILD) {
243
308
  // do one js build but not types
244
309
  build({
245
310
  skipTypes: true,
311
+ cleanOutput: true,
246
312
  })
247
313
  }
248
314
 
@@ -365,10 +431,18 @@ function swapExportsTypes(pkg, direction) {
365
431
  return swapped
366
432
  }
367
433
 
368
- async function build({ skipTypes } = {}) {
434
+ async function build({ skipTypes, cleanOutput = !shouldWatch } = {}) {
369
435
  if (process.env.DEBUG) console.info('🔹', pkg.name)
370
436
  try {
371
437
  const start = Date.now()
438
+ const isSkippingTypesForBuild = Boolean(skipTypes || shouldSkipTypes || !pkgTypes)
439
+
440
+ if (cleanOutput) {
441
+ await Promise.allSettled([
442
+ FSE.remove('dist'),
443
+ isSkippingTypesForBuild ? null : FSE.remove('types'),
444
+ ])
445
+ }
372
446
 
373
447
  const allFiles = (await fastGlob(['src/**/*.(m)?[jt]s(x)?', 'src/**/*.css'])).filter(
374
448
  (x) => !x.includes('.d.ts') && (exclude ? !x.match(exclude) : true)
@@ -441,29 +515,48 @@ async function buildTsc(allFiles) {
441
515
  const compilerOptions = createCompilerOptions(config.options, targetDir)
442
516
 
443
517
  if (config.options.isolatedDeclarations) {
444
- const oxc = require('oxc-transform')
518
+ const oxc = await import('oxc-transform')
445
519
 
446
- await Promise.all(
520
+ const results = await Promise.all(
447
521
  allFiles.map(async (file) => {
448
522
  const source = await FSE.readFile(file, 'utf-8')
449
- const { code, map } = oxc.isolatedDeclaration(file, source, {
450
- sourcemap: true,
523
+ const { code, map, errors } = await oxc.isolatedDeclaration(file, source, {
524
+ sourcemap: !shouldSkipSourceMaps,
451
525
  })
452
526
 
527
+ if (errors && errors.length > 0) {
528
+ return errors
529
+ }
530
+
453
531
  const dtsPath = path
454
532
  .join(`types`, ...file.split('/').slice(1))
455
533
  .replace(/\.tsx?$/, '.d.ts')
456
534
  const mapPath = `${dtsPath}.map`
457
535
 
458
- const output = `${code}\n//# sourceMappingURL=${path.basename(mapPath)}`
536
+ const output = shouldSkipSourceMaps
537
+ ? code
538
+ : `${code}\n//# sourceMappingURL=${path.basename(mapPath)}`
459
539
  await FSE.ensureDir(dirname(dtsPath))
460
- await Promise.all([
461
- writeIfUnchanged(dtsPath, output),
462
- writeIfUnchanged(mapPath, JSON.stringify(map, null, 2)),
463
- ])
540
+ await writeIfUnchanged(dtsPath, output)
541
+ if (!shouldSkipSourceMaps && map) {
542
+ await writeIfUnchanged(mapPath, JSON.stringify(map, null, 2))
543
+ } else {
544
+ await FSE.remove(mapPath)
545
+ }
546
+
547
+ return []
464
548
  })
465
549
  )
466
550
 
551
+ const allErrors = results.flat()
552
+
553
+ if (allErrors.length > 0) {
554
+ printOxcErrors(allErrors)
555
+ if (!shouldWatch) {
556
+ process.exit(1)
557
+ }
558
+ }
559
+
467
560
  return
468
561
  }
469
562
 
@@ -540,7 +633,7 @@ function createCompilerOptions(baseOptions, targetDir) {
540
633
  ...baseOptions,
541
634
  declaration: true,
542
635
  emitDeclarationOnly: true,
543
- declarationMap: true,
636
+ declarationMap: !shouldSkipSourceMaps,
544
637
  outDir: targetDir,
545
638
  rootDir: 'src',
546
639
  incremental: true,
@@ -701,6 +794,8 @@ async function buildJs(allFiles) {
701
794
  platform: 'web',
702
795
  bundle: shouldBundleFlag,
703
796
  specifyCJS: !avoidCJS,
797
+ keepJsOutput: avoidCJS,
798
+ preserveJsPaths: [],
704
799
  })
705
800
  : null,
706
801
 
@@ -716,6 +811,7 @@ async function buildJs(allFiles) {
716
811
  ? esbuildWriteIfChanged(esmConfig, {
717
812
  platform: 'web',
718
813
  bundle: shouldBundleFlag,
814
+ preserveJsPaths: esmAliasPaths,
719
815
  })
720
816
  : null,
721
817
 
@@ -743,6 +839,7 @@ async function buildJs(allFiles) {
743
839
  },
744
840
  {
745
841
  platform: 'web',
842
+ preserveJsPaths: esmAliasPaths,
746
843
  }
747
844
  )
748
845
  : null,
@@ -784,9 +881,11 @@ async function buildJs(allFiles) {
784
881
  async function esbuildWriteIfChanged(
785
882
  /** @type { import('esbuild').BuildOptions } */
786
883
  opts,
787
- { platform, env, specifyCJS } = {
884
+ { platform, env, specifyCJS, keepJsOutput, preserveJsPaths } = {
788
885
  platform: '',
789
886
  specifyCJS: false,
887
+ keepJsOutput: false,
888
+ preserveJsPaths: [],
790
889
  env: '',
791
890
  }
792
891
  ) {
@@ -795,6 +894,10 @@ async function esbuildWriteIfChanged(
795
894
  }
796
895
 
797
896
  const isESM = opts.target === 'esm' || opts.target === 'esnext'
897
+ const preserveJsPathSet = new Set((preserveJsPaths || []).filter(Boolean))
898
+ const preserveJsPathAbsoluteSet = new Set(
899
+ [...preserveJsPathSet].map((preserveJsPath) => path.resolve(preserveJsPath))
900
+ )
798
901
 
799
902
  const buildSettings = (() => {
800
903
  // compat with jsx and hermes back a few versions generally:
@@ -846,21 +949,20 @@ async function esbuildWriteIfChanged(
846
949
  format: isESM ? 'esm' : 'cjs',
847
950
 
848
951
  treeShaking: true,
849
- minifySyntax: true,
952
+ // We only want TAMAGUI_TARGET dead-code elimination during normal builds.
953
+ // Syntax minification can legally rewrite statements into comma expressions.
954
+ minifySyntax: false,
850
955
  write: false,
851
956
 
852
957
  color: true,
853
958
  allowOverwrite: true,
854
959
  keepNames: false,
855
- sourcemap: true,
960
+ sourcemap: !shouldSkipSourceMaps,
856
961
  sourcesContent: false,
857
962
  logLevel: 'error',
858
963
  ...(platform === 'native' && nativeEsbuildSettings),
859
964
  ...(platform === 'web' && webEsbuildSettings),
860
965
  define: {
861
- ...(platform && {
862
- 'process.env.TAMAGUI_TARGET': `"${platform}"`,
863
- }),
864
966
  ...(env && {
865
967
  'process.env.NODE_ENV': `"${env}"`,
866
968
  }),
@@ -962,23 +1064,43 @@ async function esbuildWriteIfChanged(
962
1064
  }
963
1065
  }
964
1066
 
965
- if (pkgRemoveSideEffects && isESM) {
966
- const allowedSideEffects = pkg.sideEffects || []
1067
+ if (!isMap && path.endsWith('.js')) {
1068
+ contents = dceTamaguiTarget(contents, {
1069
+ format: opts.format,
1070
+ jsx: opts.jsx,
1071
+ platform,
1072
+ })
1073
+ }
1074
+
1075
+ if (isESM && pkg.sideEffects !== true && pkg.sideEffects !== undefined) {
1076
+ const sideEffects = pkg.sideEffects
1077
+ // sideEffects: false means nothing has side effects, strip all bare imports
1078
+ // sideEffects: ["*.css", ...] means only matching files have side effects
1079
+ const picomatch = require('picomatch')
1080
+ const matchers = sideEffects === false
1081
+ ? null // strip everything
1082
+ : (Array.isArray(sideEffects) ? sideEffects : []).map((p) => picomatch(p))
967
1083
 
968
1084
  const result = []
969
1085
  const lines = contents.split('\n')
970
1086
  for (const line of lines) {
971
- if (
972
- !line.startsWith('import ') ||
973
- allowedSideEffects.some((allowed) => line.includes(allowed))
974
- ) {
1087
+ // only process bare imports: import "...";
1088
+ const match = line.match(/^import\s+["']([^"']+)["'];?$/)
1089
+ if (!match) {
975
1090
  result.push(line)
976
1091
  continue
977
1092
  }
978
- result.push(line.replace(/import "[^"]+";/g, ''))
1093
+ const specifier = match[1].replace(/^\.\//, '')
1094
+ // sideEffects: false → strip all bare imports
1095
+ if (matchers === null) {
1096
+ result.push('')
1097
+ continue
1098
+ }
1099
+ // sideEffects: [...] → keep if specifier matches any pattern
1100
+ const hasSideEffect = matchers.some((m) => m(specifier))
1101
+ result.push(hasSideEffect ? line : '')
979
1102
  }
980
1103
 
981
- // match whitespace to preserve sourcemaps
982
1104
  contents = result.join('\n')
983
1105
  }
984
1106
 
@@ -1018,7 +1140,7 @@ async function esbuildWriteIfChanged(
1018
1140
  : transform(contents, {
1019
1141
  filename: path,
1020
1142
  configFile: false,
1021
- sourceMap: true,
1143
+ sourceMap: !shouldSkipSourceMaps,
1022
1144
  plugins: [
1023
1145
  [
1024
1146
  require.resolve('@tamagui/babel-plugin-fully-specified/commonjs'),
@@ -1029,11 +1151,28 @@ async function esbuildWriteIfChanged(
1029
1151
  ].filter(Boolean),
1030
1152
  })
1031
1153
 
1032
- cleanupNonCjsFiles.push(path)
1154
+ const shouldPreserveJsAlias =
1155
+ preserveJsPathSet.has(path) || preserveJsPathAbsoluteSet.has(path)
1156
+
1157
+ if (!shouldPreserveJsAlias) {
1158
+ cleanupNonCjsFiles.push(path)
1159
+ cleanupNonCjsFiles.push(path + '.map')
1160
+ }
1033
1161
 
1034
1162
  await flush(path.replace(/\.js$/, '.cjs'), result.code)
1163
+
1164
+ if (shouldPreserveJsAlias) {
1165
+ await flush(path, result.code)
1166
+ }
1035
1167
  })
1036
1168
  )
1169
+ if (cleanupNonCjsFiles.length) {
1170
+ await Promise.all(cleanupNonCjsFiles.map(async (file) => FSE.remove(file)))
1171
+ }
1172
+ return
1173
+ }
1174
+
1175
+ if (keepJsOutput) {
1037
1176
  return
1038
1177
  }
1039
1178
 
@@ -1060,7 +1199,7 @@ async function esbuildWriteIfChanged(
1060
1199
  : transform(contents, {
1061
1200
  filename: newOutPath,
1062
1201
  configFile: false,
1063
- sourceMap: true,
1202
+ sourceMap: !shouldSkipSourceMaps,
1064
1203
  plugins: [
1065
1204
  [
1066
1205
  isESM
@@ -1074,7 +1213,10 @@ async function esbuildWriteIfChanged(
1074
1213
  ].filter(Boolean),
1075
1214
  })
1076
1215
 
1077
- if (!path.includes('.native.')) {
1216
+ const shouldPreserveJsAlias =
1217
+ preserveJsPathSet.has(path) || preserveJsPathAbsoluteSet.has(path)
1218
+
1219
+ if (!path.includes('.native.') && !shouldPreserveJsAlias) {
1078
1220
  cleanupNonMjsFiles.push(path)
1079
1221
  cleanupNonMjsFiles.push(path + '.map')
1080
1222
  }
@@ -1083,25 +1225,39 @@ async function esbuildWriteIfChanged(
1083
1225
  await flush(
1084
1226
  newOutPath,
1085
1227
  result.code +
1086
- (result.map ? `\n//# sourceMappingURL=${basename(newOutPath)}.map\n` : '')
1228
+ (result.map && !shouldSkipSourceMaps
1229
+ ? `\n//# sourceMappingURL=${basename(newOutPath)}.map\n`
1230
+ : '')
1087
1231
  )
1088
- if (result.map) {
1232
+ if (result.map && !shouldSkipSourceMaps) {
1089
1233
  await flush(newOutPath + '.map', JSON.stringify(result.map))
1234
+ } else {
1235
+ await FSE.remove(newOutPath + '.map')
1236
+ }
1237
+
1238
+ if (shouldPreserveJsAlias) {
1239
+ await flush(
1240
+ path,
1241
+ result.code +
1242
+ (result.map && !shouldSkipSourceMaps
1243
+ ? `\n//# sourceMappingURL=${basename(path)}.map\n`
1244
+ : '')
1245
+ )
1246
+ if (result.map && !shouldSkipSourceMaps) {
1247
+ await flush(path + '.map', JSON.stringify(result.map))
1248
+ } else {
1249
+ await FSE.remove(path + '.map')
1250
+ }
1090
1251
  }
1091
1252
  })
1092
1253
  )
1093
1254
 
1094
- // if we do mjs we should remove js after to avoid bloat
1095
- if (
1096
- process.env.TAMAGUI_BUILD_REMOVE_ESM_JS_FILES ||
1097
- process.env.TAMAGUI_BUILD_CLEANUP_JS_FILES
1098
- ) {
1099
- if (cleanupNonMjsFiles.length) {
1100
- await Promise.all(
1101
- [...cleanupNonMjsFiles, ...cleanupNonCjsFiles].map(async (file) => {
1102
- await FSE.remove(file)
1103
- })
1104
- )
1105
- }
1255
+ // remove intermediary .js files once the final .mjs/.cjs outputs exist
1256
+ if (cleanupNonMjsFiles.length || cleanupNonCjsFiles.length) {
1257
+ await Promise.all(
1258
+ [...cleanupNonMjsFiles, ...cleanupNonCjsFiles].map(async (file) => {
1259
+ await FSE.remove(file)
1260
+ })
1261
+ )
1106
1262
  }
1107
1263
  }