Skip to content

Commit 1c7c276

Browse files
authored
refactor(angular-query): migrate from tsup to vite (#9281)
* chore(angular-query): migrate from tsup to vite * build fixes
1 parent 33d008b commit 1c7c276

File tree

10 files changed

+404
-37
lines changed

10 files changed

+404
-37
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"ignoreRules": ["cjs-resolves-to-esm", "no-resolution"]
2+
"ignoreRules": ["cjs-resolves-to-esm"]
33
}

packages/angular-query-experimental/package.json

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"tanstack"
2828
],
2929
"scripts": {
30-
"clean": "premove ./build ./coverage ./dist-ts",
30+
"clean": "premove ./build ./coverage ./dist-ts ./**.d.ts",
3131
"compile": "tsc --build",
3232
"test:eslint": "eslint ./src",
3333
"test:types": "npm-run-all --serial test:types:*",
@@ -42,28 +42,29 @@
4242
"test:types:tscurrent": "tsc --build",
4343
"test:lib": "vitest",
4444
"test:lib:dev": "pnpm run test:lib --watch",
45-
"test:build": "publint --strict && attw --pack",
46-
"build": "pnpm build:tsup",
47-
"build:tsup": "tsup --tsconfig tsconfig.prod.json"
45+
"test:build": "pnpm pack && publint *.tgz --strict && attw *.tgz; premove *.tgz",
46+
"build": "vite build",
47+
"prepack": "node scripts/prepack.js",
48+
"postpack": "node scripts/postpack.js"
4849
},
4950
"type": "module",
50-
"types": "build/index.d.ts",
51-
"module": "build/index.mjs",
51+
"types": "dist/types/index.d.ts",
52+
"module": "dist/index.mjs",
5253
"exports": {
5354
".": {
55+
"types": "./dist/types/index.d.ts",
5456
"@tanstack/custom-condition": "./src/index.ts",
55-
"types": "./build/index.d.ts",
56-
"default": "./build/index.mjs"
57+
"default": "./dist/index.mjs"
5758
},
5859
"./package.json": {
5960
"default": "./package.json"
6061
}
6162
},
6263
"sideEffects": false,
6364
"files": [
64-
"build",
65-
"src",
66-
"!src/__tests__"
65+
"**/*.d.ts",
66+
"dist",
67+
"!dist/types/**"
6768
],
6869
"dependencies": {
6970
"@tanstack/query-core": "workspace:*",
@@ -77,10 +78,25 @@
7778
"@angular/platform-browser-dynamic": "^20.0.0",
7879
"@tanstack/query-test-utils": "workspace:*",
7980
"eslint-plugin-jsdoc": "^50.5.0",
80-
"npm-run-all2": "^5.0.0"
81+
"npm-run-all2": "^5.0.0",
82+
"vite-plugin-dts": "4.2.3",
83+
"vite-plugin-externalize-deps": "^0.9.0",
84+
"vite-tsconfig-paths": "^5.1.4"
8185
},
8286
"peerDependencies": {
8387
"@angular/common": ">=16.0.0",
8488
"@angular/core": ">=16.0.0"
89+
},
90+
"publishConfig": {
91+
"types": "index.d.ts",
92+
"exports": {
93+
".": {
94+
"types": "./index.d.ts",
95+
"default": "./dist/index.mjs"
96+
},
97+
"./package.json": {
98+
"default": "./package.json"
99+
}
100+
}
85101
}
86102
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { unlink, rmdir } from 'fs/promises'
2+
import fg from 'fast-glob'
3+
4+
const constants = {
5+
IGNORE_FILES_PATTERNS: [
6+
'dist/**',
7+
'node_modules/**',
8+
'.git/**',
9+
'scripts/**',
10+
],
11+
CLEANUP_FILES_GLOB: ['**/*.d.ts'],
12+
IGNORE_REMOVE_DIRECTORIES: [
13+
'dist/**',
14+
'node_modules/**',
15+
'.git/**',
16+
'scripts/**',
17+
'src/**',
18+
],
19+
}
20+
21+
async function postpack() {
22+
console.log(
23+
'Running postpack script to cleanup type declaration linked files used for publishing',
24+
)
25+
26+
const typeFiles = await fg(constants.CLEANUP_FILES_GLOB, {
27+
ignore: constants.IGNORE_FILES_PATTERNS,
28+
})
29+
30+
if (typeFiles.length === 0) {
31+
return
32+
}
33+
34+
await Promise.all(typeFiles.map((file) => unlink(file)))
35+
36+
const dirs = await fg(['**/'], {
37+
onlyDirectories: true,
38+
ignore: constants.IGNORE_REMOVE_DIRECTORIES,
39+
})
40+
41+
// Remove empty directories (deepest first)
42+
const sortedDirs = dirs.sort(
43+
(a, b) => b.split('/').length - a.split('/').length,
44+
)
45+
await Promise.all(
46+
sortedDirs.map(
47+
(dir) => rmdir(dir).catch(() => {}), // Ignore errors (dir not empty)
48+
),
49+
)
50+
}
51+
52+
postpack().catch((error) => {
53+
console.error('Postpack failed:', error)
54+
})
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { link, mkdir } from 'fs/promises'
2+
import { dirname, relative } from 'path'
3+
import fg from 'fast-glob'
4+
5+
const constants = {
6+
DIST_TYPES_DIRECTORY: 'dist/types',
7+
OUTPUT_DIRECTORY: '.',
8+
DIST_TYPE_FILES_GLOB: 'dist/types/**/*.d.ts',
9+
}
10+
11+
/*
12+
`prepack` lifecycle script which links type declaration files from the dist folder to the package root.
13+
allows using types in package exports as such:
14+
15+
`"types": "./index.d.ts"`
16+
17+
and subpath exports
18+
19+
```json
20+
"./some-subpath": {
21+
"types": "./some-subpath/index.d.ts", // ✅ works with `"modeResolution": "node"`
22+
"default": "./build/some-subpath/index.mjs"
23+
},
24+
```
25+
26+
When TypeScript is configured with `moduleResolution: node`, type declaration file directory structures are expected
27+
to exactly match the subpath export as in the example above.
28+
29+
```json
30+
"./some-subpath": {
31+
"types": "./build/dist/some-subpath/index.d.ts", // ❌ does not work with `"moduleResolution": "node"`
32+
"default": "./build/some-subpath/index.mjs"
33+
},
34+
```
35+
36+
It's important to support `"moduleResolution": "node"` as many Angular applications are still configured this way.
37+
38+
In the `postpack` lifecycle script these links are removed to keep a clean development environment
39+
*/
40+
async function prepack() {
41+
console.log('Running prepack script to prepare types for publishing')
42+
43+
const typeFiles = await fg([constants.DIST_TYPE_FILES_GLOB])
44+
if (typeFiles.length === 0) return
45+
46+
const destDirs = [
47+
...new Set(
48+
typeFiles
49+
.map((file) => {
50+
const dest = relative(constants.DIST_TYPES_DIRECTORY, file)
51+
return dirname(dest)
52+
})
53+
.filter((dir) => dir !== constants.OUTPUT_DIRECTORY),
54+
),
55+
]
56+
57+
await Promise.all(destDirs.map((dir) => mkdir(dir, { recursive: true })))
58+
await Promise.all(
59+
typeFiles.map((file) => {
60+
const dest = relative(constants.DIST_TYPES_DIRECTORY, file)
61+
return link(file, dest)
62+
}),
63+
)
64+
65+
console.log(`Linked ${typeFiles.length} type files`)
66+
}
67+
68+
prepack().catch(console.error)

packages/angular-query-experimental/tsconfig.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
"compilerOptions": {
44
"outDir": "./dist-ts",
55
"rootDir": ".",
6-
"noImplicitOverride": true,
7-
"noPropertyAccessFromIndexSignature": true,
86
"noFallthroughCasesInSwitch": true,
97
"useDefineForClassFields": false,
108
"target": "ES2022"

packages/angular-query-experimental/tsconfig.prod.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"compilerOptions": {
44
"incremental": false,
55
"composite": false,
6-
"rootDir": "../../"
6+
"rootDir": "../../",
7+
"customConditions": null
78
}
89
}

packages/angular-query-experimental/tsup.config.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

packages/angular-query-experimental/vite.config.ts

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
1-
import { defineConfig } from 'vitest/config'
2-
1+
import { defineConfig, mergeConfig } from 'vitest/config'
2+
import { externalizeDeps } from 'vite-plugin-externalize-deps'
3+
import tsconfigPaths from 'vite-tsconfig-paths'
4+
import dts from 'vite-plugin-dts'
35
import packageJson from './package.json'
6+
import type { Options } from '@tanstack/config/vite'
7+
8+
function ensureImportFileExtension({
9+
content,
10+
extension,
11+
}: {
12+
content: string
13+
extension: string
14+
}) {
15+
// replace e.g. `import { foo } from './foo'` with `import { foo } from './foo.js'`
16+
content = content.replace(
17+
/(im|ex)port\s[\w{}/*\s,]+from\s['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm,
18+
`$&.${extension}`,
19+
)
20+
21+
// replace e.g. `import('./foo')` with `import('./foo.js')`
22+
content = content.replace(
23+
/import\(['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm,
24+
`$&.${extension}`,
25+
)
26+
return content
27+
}
428

5-
export default defineConfig({
29+
const config = defineConfig({
630
// fix from https://github.com/vitest-dev/vitest/issues/6992#issuecomment-2509408660
731
resolve: {
832
conditions: ['@tanstack/custom-condition'],
@@ -26,3 +50,73 @@ export default defineConfig({
2650
restoreMocks: true,
2751
},
2852
})
53+
54+
// copy from @tanstack/config/vite with changes:
55+
// - dts outDir: dist/types
56+
// - build - lib - fileName: [name.mjs]
57+
// - rollup - output - preserveModulesRoot: src
58+
export const tanstackViteConfig = (options: Options) => {
59+
const outDir = options.outDir ?? 'dist'
60+
const cjs = options.cjs ?? true
61+
62+
return defineConfig({
63+
plugins: [
64+
externalizeDeps({ include: options.externalDeps ?? [] }),
65+
tsconfigPaths({
66+
projects: options.tsconfigPath ? [options.tsconfigPath] : undefined,
67+
}),
68+
dts({
69+
outDir: `dist/types`,
70+
entryRoot: options.srcDir,
71+
include: options.srcDir,
72+
exclude: options.exclude,
73+
tsconfigPath: options.tsconfigPath,
74+
compilerOptions: {
75+
module: 99, // ESNext
76+
declarationMap: false,
77+
},
78+
beforeWriteFile: (filePath, content) => {
79+
// content =
80+
// options.beforeWriteDeclarationFile?.(filePath, content) || content
81+
return {
82+
filePath,
83+
content: ensureImportFileExtension({ content, extension: 'js' }),
84+
}
85+
},
86+
afterDiagnostic: (diagnostics) => {
87+
if (diagnostics.length > 0) {
88+
console.error('Please fix the above type errors')
89+
process.exit(1)
90+
}
91+
},
92+
}),
93+
],
94+
build: {
95+
outDir,
96+
minify: false,
97+
sourcemap: true,
98+
lib: {
99+
entry: options.entry,
100+
formats: cjs ? ['es', 'cjs'] : ['es'],
101+
fileName: () => '[name].mjs',
102+
},
103+
rollupOptions: {
104+
output: {
105+
preserveModules: true,
106+
preserveModulesRoot: 'src',
107+
},
108+
},
109+
},
110+
})
111+
}
112+
113+
export default mergeConfig(
114+
config,
115+
tanstackViteConfig({
116+
cjs: false,
117+
entry: ['./src/index.ts'],
118+
exclude: ['src/__tests__'],
119+
srcDir: './src',
120+
tsconfigPath: 'tsconfig.prod.json',
121+
}),
122+
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"ignoreRules": ["cjs-resolves-to-esm", "no-resolution"]
2+
"ignoreRules": ["cjs-resolves-to-esm"]
33
}

0 commit comments

Comments
 (0)