Skip to content

refactor(angular-query): migrate from tsup to vite #9281

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/angular-query-experimental/.attw.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"ignoreRules": ["cjs-resolves-to-esm", "no-resolution"]
"ignoreRules": ["cjs-resolves-to-esm"]
}
40 changes: 28 additions & 12 deletions packages/angular-query-experimental/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"tanstack"
],
"scripts": {
"clean": "premove ./build ./coverage ./dist-ts",
"clean": "premove ./build ./coverage ./dist-ts ./**.d.ts",
"compile": "tsc --build",
"test:eslint": "eslint ./src",
"test:types": "npm-run-all --serial test:types:*",
Expand All @@ -42,28 +42,29 @@
"test:types:tscurrent": "tsc --build",
"test:lib": "vitest",
"test:lib:dev": "pnpm run test:lib --watch",
"test:build": "publint --strict && attw --pack",
"build": "pnpm build:tsup",
"build:tsup": "tsup --tsconfig tsconfig.prod.json"
"test:build": "pnpm pack && publint *.tgz --strict && attw *.tgz; premove *.tgz",
"build": "vite build",
"prepack": "node scripts/prepack.js",
"postpack": "node scripts/postpack.js"
},
"type": "module",
"types": "build/index.d.ts",
"module": "build/index.mjs",
"types": "dist/types/index.d.ts",
"module": "dist/index.mjs",
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"@tanstack/custom-condition": "./src/index.ts",
"types": "./build/index.d.ts",
"default": "./build/index.mjs"
"default": "./dist/index.mjs"
},
"./package.json": {
"default": "./package.json"
}
},
"sideEffects": false,
"files": [
"build",
"src",
"!src/__tests__"
"**/*.d.ts",
"dist",
"!dist/types/**"
],
"dependencies": {
"@tanstack/query-core": "workspace:*",
Expand All @@ -77,10 +78,25 @@
"@angular/platform-browser-dynamic": "^20.0.0",
"@tanstack/query-test-utils": "workspace:*",
"eslint-plugin-jsdoc": "^50.5.0",
"npm-run-all2": "^5.0.0"
"npm-run-all2": "^5.0.0",
"vite-plugin-dts": "4.2.3",
"vite-plugin-externalize-deps": "^0.9.0",
"vite-tsconfig-paths": "^5.1.4"
},
"peerDependencies": {
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0"
},
"publishConfig": {
"types": "index.d.ts",
"exports": {
".": {
"types": "./index.d.ts",
"default": "./dist/index.mjs"
},
"./package.json": {
"default": "./package.json"
}
}
}
}
54 changes: 54 additions & 0 deletions packages/angular-query-experimental/scripts/postpack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { unlink, rmdir } from 'fs/promises'
import fg from 'fast-glob'

const constants = {
IGNORE_FILES_PATTERNS: [
'dist/**',
'node_modules/**',
'.git/**',
'scripts/**',
],
CLEANUP_FILES_GLOB: ['**/*.d.ts'],
IGNORE_REMOVE_DIRECTORIES: [
'dist/**',
'node_modules/**',
'.git/**',
'scripts/**',
'src/**',
],
}

async function postpack() {
console.log(
'Running postpack script to cleanup type declaration linked files used for publishing',
)

const typeFiles = await fg(constants.CLEANUP_FILES_GLOB, {
ignore: constants.IGNORE_FILES_PATTERNS,
})

if (typeFiles.length === 0) {
return
}

await Promise.all(typeFiles.map((file) => unlink(file)))

const dirs = await fg(['**/'], {
onlyDirectories: true,
ignore: constants.IGNORE_REMOVE_DIRECTORIES,
})

// Remove empty directories (deepest first)
const sortedDirs = dirs.sort(
(a, b) => b.split('/').length - a.split('/').length,
)
await Promise.all(
sortedDirs.map(
(dir) => rmdir(dir).catch(() => {}), // Ignore errors (dir not empty)
),
)
}

postpack().catch((error) => {
console.error('Postpack failed:', error)
})
68 changes: 68 additions & 0 deletions packages/angular-query-experimental/scripts/prepack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { link, mkdir } from 'fs/promises'
import { dirname, relative } from 'path'
import fg from 'fast-glob'

const constants = {
DIST_TYPES_DIRECTORY: 'dist/types',
OUTPUT_DIRECTORY: '.',
DIST_TYPE_FILES_GLOB: 'dist/types/**/*.d.ts',
}

/*
`prepack` lifecycle script which links type declaration files from the dist folder to the package root.
allows using types in package exports as such:

`"types": "./index.d.ts"`

and subpath exports

```json
"./some-subpath": {
"types": "./some-subpath/index.d.ts", // ✅ works with `"modeResolution": "node"`
"default": "./build/some-subpath/index.mjs"
},
```

When TypeScript is configured with `moduleResolution: node`, type declaration file directory structures are expected
to exactly match the subpath export as in the example above.

```json
"./some-subpath": {
"types": "./build/dist/some-subpath/index.d.ts", // ❌ does not work with `"moduleResolution": "node"`
"default": "./build/some-subpath/index.mjs"
},
```

It's important to support `"moduleResolution": "node"` as many Angular applications are still configured this way.

In the `postpack` lifecycle script these links are removed to keep a clean development environment
*/
async function prepack() {
console.log('Running prepack script to prepare types for publishing')

const typeFiles = await fg([constants.DIST_TYPE_FILES_GLOB])
if (typeFiles.length === 0) return

const destDirs = [
...new Set(
typeFiles
.map((file) => {
const dest = relative(constants.DIST_TYPES_DIRECTORY, file)
return dirname(dest)
})
.filter((dir) => dir !== constants.OUTPUT_DIRECTORY),
),
]

await Promise.all(destDirs.map((dir) => mkdir(dir, { recursive: true })))
await Promise.all(
typeFiles.map((file) => {
const dest = relative(constants.DIST_TYPES_DIRECTORY, file)
return link(file, dest)
}),
)

console.log(`Linked ${typeFiles.length} type files`)
}

prepack().catch(console.error)
2 changes: 0 additions & 2 deletions packages/angular-query-experimental/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
"compilerOptions": {
"outDir": "./dist-ts",
"rootDir": ".",
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noFallthroughCasesInSwitch": true,
"useDefineForClassFields": false,
"target": "ES2022"
Expand Down
3 changes: 2 additions & 1 deletion packages/angular-query-experimental/tsconfig.prod.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"compilerOptions": {
"incremental": false,
"composite": false,
"rootDir": "../../"
"rootDir": "../../",
"customConditions": null
}
}
13 changes: 0 additions & 13 deletions packages/angular-query-experimental/tsup.config.js

This file was deleted.

100 changes: 97 additions & 3 deletions packages/angular-query-experimental/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,32 @@
import { defineConfig } from 'vitest/config'

import { defineConfig, mergeConfig } from 'vitest/config'
import { externalizeDeps } from 'vite-plugin-externalize-deps'
import tsconfigPaths from 'vite-tsconfig-paths'
import dts from 'vite-plugin-dts'
import packageJson from './package.json'
import type { Options } from '@tanstack/config/vite'

function ensureImportFileExtension({
content,
extension,
}: {
content: string
extension: string
}) {
// replace e.g. `import { foo } from './foo'` with `import { foo } from './foo.js'`
content = content.replace(
/(im|ex)port\s[\w{}/*\s,]+from\s['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm,
`$&.${extension}`,
)

// replace e.g. `import('./foo')` with `import('./foo.js')`
content = content.replace(
/import\(['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm,
`$&.${extension}`,
)
return content
}

export default defineConfig({
const config = defineConfig({
// fix from https://github.com/vitest-dev/vitest/issues/6992#issuecomment-2509408660
resolve: {
conditions: ['@tanstack/custom-condition'],
Expand All @@ -26,3 +50,73 @@ export default defineConfig({
restoreMocks: true,
},
})

// copy from @tanstack/config/vite with changes:
// - dts outDir: dist/types
// - build - lib - fileName: [name.mjs]
// - rollup - output - preserveModulesRoot: src
export const tanstackViteConfig = (options: Options) => {
const outDir = options.outDir ?? 'dist'
const cjs = options.cjs ?? true

return defineConfig({
plugins: [
externalizeDeps({ include: options.externalDeps ?? [] }),
tsconfigPaths({
projects: options.tsconfigPath ? [options.tsconfigPath] : undefined,
}),
dts({
outDir: `dist/types`,
entryRoot: options.srcDir,
include: options.srcDir,
exclude: options.exclude,
tsconfigPath: options.tsconfigPath,
compilerOptions: {
module: 99, // ESNext
declarationMap: false,
},
beforeWriteFile: (filePath, content) => {
// content =
// options.beforeWriteDeclarationFile?.(filePath, content) || content
return {
filePath,
content: ensureImportFileExtension({ content, extension: 'js' }),
}
},
afterDiagnostic: (diagnostics) => {
if (diagnostics.length > 0) {
console.error('Please fix the above type errors')
process.exit(1)
}
},
}),
],
build: {
outDir,
minify: false,
sourcemap: true,
lib: {
entry: options.entry,
formats: cjs ? ['es', 'cjs'] : ['es'],
fileName: () => '[name].mjs',
},
rollupOptions: {
output: {
preserveModules: true,
preserveModulesRoot: 'src',
},
},
},
})
}

export default mergeConfig(
config,
tanstackViteConfig({
cjs: false,
entry: ['./src/index.ts'],
exclude: ['src/__tests__'],
srcDir: './src',
tsconfigPath: 'tsconfig.prod.json',
}),
)
2 changes: 1 addition & 1 deletion packages/angular-query-persist-client/.attw.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"ignoreRules": ["cjs-resolves-to-esm", "no-resolution"]
"ignoreRules": ["cjs-resolves-to-esm"]
}
Loading
Loading