Skip to content

Commit

Permalink
feat: Add Vite build config (#2)
Browse files Browse the repository at this point in the history
* feat: Add vite build config export

* Fix exports

* Add declaration

* Fix prettier

* Add react integration

* Add CJS test

* Add documentation

* Update deps

* Update tsconfig.json and docs

* Remove duplicated deps

* Update docs

* Add more src files to react integration

* Add vue integration

* Add vue snapshot

* Disable declarationMap until dtsExtension added to vite-plugin-dts

See: qmhc/vite-plugin-dts#292

* Update integration snapshots

* docs: Update vite instructions
  • Loading branch information
lachlancollins committed Jan 3, 2024
1 parent d504d6f commit f3bd6a9
Show file tree
Hide file tree
Showing 81 changed files with 2,317 additions and 134 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Expand Up @@ -8,8 +8,6 @@ yarn.lock

# builds
types
build
*/build
dist
lib
es
Expand Down Expand Up @@ -48,3 +46,5 @@ nx-cloud.env

.nx/cache
.tsup
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
2 changes: 1 addition & 1 deletion .prettierignore
Expand Up @@ -4,5 +4,5 @@
**/coverage
**/dist
**/docs
**/codemods/**/__testfixtures__
**/snap
pnpm-lock.yaml
66 changes: 66 additions & 0 deletions docs/build.md
@@ -0,0 +1,66 @@
---
id: build
title: Build
---

The Vite build setup provided is the culmination of several attempts to make the TanStack libraries work in all environments, including ESM, CJS, the various TypeScript module resolution options, and a diverse bundler ecosystem.

# Usage

The build config is quite opinionated, as it is designed to work with our internal libraries. If you follow the below instructions, it _may_ work for your library too!

## package.json

- Ensure `"type": "module"` is set.
- Ensure you have [Vite](https://www.npmjs.com/package/vite) installed. Installing [Publint](https://www.npmjs.com/package/publint) is also recommended.
- Change your build script to `"build": "vite build && publint --strict"`
- Ensure you have an `"exports"` field. We use this, but you might have different requirements:

```json
{
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
},
"./package.json": "./package.json"
}
}
```

## tsconfig.json

- Ensure your `"include"` field includes `"vite.config.ts"`.
- Set `"moduleResolution"` to `"bundler"`.

## vite.config.ts

- Wrap your `defineConfig` in `mergeConfig` (also exported from Vite).
- Add `tanstackBuildConfig` into `mergeConfig`.
- Note: Please avoid modifying `build` in your own `defineConfig`.
- See an example below:

```ts
import { defineConfig, mergeConfig } from 'vite'
import { tanstackBuildConfig } from '@tanstack/config/build'

export default mergeConfig(
tanstackBuildConfig({
entry: 'src/index.ts',
srcDir: 'src',
}),
defineConfig({
// Add any custom options here, such as framework plugins
}),
)
```

# Caveats

While this config _will_ work with most frameworks with a Vite adapter, it doesn't mean you _should_ use it for all frameworks. For instance, Svelte publishes [@sveltejs/package](https://www.npmjs.com/package/@sveltejs/package), and Angular publishes [ng-packagr](https://www.npmjs.com/package/ng-packagr). When a framework-specific build tool exists, this should be preferred.
30 changes: 30 additions & 0 deletions integrations/react/README.md
@@ -0,0 +1,30 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
}
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
18 changes: 18 additions & 0 deletions integrations/react/package.json
@@ -0,0 +1,18 @@
{
"name": "react-integration",
"private": true,
"type": "module",
"scripts": {
"test:build": "vite build && vitest"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@tanstack/config": "<1.0.0",
"@types/react": "^18.2.46",
"@types/react-dom": "^18.2.18",
"@vitejs/plugin-react": "^4.2.1"
}
}
7 changes: 7 additions & 0 deletions integrations/react/snap/cjs/index.cjs
@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const useClient = require("./use-client.cjs");
const nested = require("./nested/nested.cjs");
exports.Component = useClient.Component;
exports.test = nested.test;
//# sourceMappingURL=index.cjs.map
1 change: 1 addition & 0 deletions integrations/react/snap/cjs/index.cjs.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions integrations/react/snap/cjs/index.d.cts
@@ -0,0 +1,2 @@
export * from './use-client';
export * from './nested/nested';
5 changes: 5 additions & 0 deletions integrations/react/snap/cjs/nested/nested.cjs
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const test = "Hello world!";
exports.test = test;
//# sourceMappingURL=nested.cjs.map
1 change: 1 addition & 0 deletions integrations/react/snap/cjs/nested/nested.cjs.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integrations/react/snap/cjs/nested/nested.d.cts
@@ -0,0 +1 @@
export declare const test = "Hello world!";
8 changes: 8 additions & 0 deletions integrations/react/snap/cjs/use-client.cjs
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const jsxRuntime = require("react/jsx-runtime");
const Component = () => {
return /* @__PURE__ */ jsxRuntime.jsx("div", { children: "Hello world!" });
};
exports.Component = Component;
//# sourceMappingURL=use-client.cjs.map
1 change: 1 addition & 0 deletions integrations/react/snap/cjs/use-client.cjs.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integrations/react/snap/cjs/use-client.d.cts
@@ -0,0 +1 @@
export declare const Component: () => import("react/jsx-runtime").JSX.Element;
2 changes: 2 additions & 0 deletions integrations/react/snap/esm/index.d.ts
@@ -0,0 +1,2 @@
export * from './use-client';
export * from './nested/nested';
7 changes: 7 additions & 0 deletions integrations/react/snap/esm/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integrations/react/snap/esm/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integrations/react/snap/esm/nested/nested.d.ts
@@ -0,0 +1 @@
export declare const test = "Hello world!";
5 changes: 5 additions & 0 deletions integrations/react/snap/esm/nested/nested.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integrations/react/snap/esm/nested/nested.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integrations/react/snap/esm/use-client.d.ts
@@ -0,0 +1 @@
export declare const Component: () => import("react/jsx-runtime").JSX.Element;
8 changes: 8 additions & 0 deletions integrations/react/snap/esm/use-client.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions integrations/react/snap/esm/use-client.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions integrations/react/src/index.ts
@@ -0,0 +1,2 @@
export * from './use-client'
export * from './nested/nested'
1 change: 1 addition & 0 deletions integrations/react/src/nested/nested.ts
@@ -0,0 +1 @@
export const test = 'Hello world!'
5 changes: 5 additions & 0 deletions integrations/react/src/use-client.tsx
@@ -0,0 +1,5 @@
'use client'

export const Component = () => {
return <div>Hello world!</div>
}
34 changes: 34 additions & 0 deletions integrations/react/tests/build.test.ts
@@ -0,0 +1,34 @@
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { dirname, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'

const __dirname = dirname(fileURLToPath(import.meta.url))
const rootDir = resolve(__dirname, '..')

const esmExtensions = ['.js', '.js.map', '.d.ts']
const cjsExtensions = ['.cjs', '.cjs.map', '.d.cts']

const files = ['index', 'use-client', 'nested/nested']

describe('Check React build output', () => {
it('should build the same ESM output', () => {
files.forEach((file) => {
esmExtensions.forEach((ext) => {
expect(
readFileSync(`${rootDir}/dist/esm/${file}${ext}`).toString(),
).toMatchFileSnapshot(`${rootDir}/snap/esm/${file}${ext}`)
})
})
})

it('should build the same CJS output', () => {
files.forEach((file) => {
cjsExtensions.forEach((ext) => {
expect(
readFileSync(`${rootDir}/dist/cjs/${file}${ext}`).toString(),
).toMatchFileSnapshot(`${rootDir}/snap/cjs/${file}${ext}`)
})
})
})
})
24 changes: 24 additions & 0 deletions integrations/react/tsconfig.json
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src", "tests", "vite.config.ts"]
}
18 changes: 18 additions & 0 deletions integrations/react/vite.config.ts
@@ -0,0 +1,18 @@
import { mergeConfig, defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import { tanstackBuildConfig } from '@tanstack/config/build'

export default mergeConfig(
tanstackBuildConfig({
entry: 'src/index.ts',
srcDir: 'src',
exclude: ['src/__tests__'],
}),
defineConfig({
plugins: [react()],
test: {
name: 'react',
watch: false,
},
}),
)
18 changes: 18 additions & 0 deletions integrations/vue/README.md
@@ -0,0 +1,18 @@
# Vue 3 + TypeScript + Vite

This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.

## Recommended IDE Setup

- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).

## Type Support For `.vue` Imports in TS

TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.

If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:

1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
15 changes: 15 additions & 0 deletions integrations/vue/package.json
@@ -0,0 +1,15 @@
{
"name": "vue-integration",
"private": true,
"type": "module",
"scripts": {
"test:build": "vite build && vitest"
},
"dependencies": {
"vue": "^3.3.11"
},
"devDependencies": {
"@tanstack/config": "<1.0.0",
"@vitejs/plugin-vue": "^4.5.2"
}
}
7 changes: 7 additions & 0 deletions integrations/vue/snap/cjs/App.vue.cjs
@@ -0,0 +1,7 @@
"use strict";
const App_vue_vue_type_script_setup_true_lang = require("./App.vue2.cjs");
require("./App.vue3.cjs");
const _pluginVue_exportHelper = require("./_virtual/_plugin-vue_export-helper.cjs");
const App = /* @__PURE__ */ _pluginVue_exportHelper(App_vue_vue_type_script_setup_true_lang, [["__scopeId", "data-v-57d0a178"]]);
module.exports = App;
//# sourceMappingURL=App.vue.cjs.map
1 change: 1 addition & 0 deletions integrations/vue/snap/cjs/App.vue.cjs.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions integrations/vue/snap/cjs/App.vue.d.cts
@@ -0,0 +1,2 @@
declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
export default _default;
13 changes: 13 additions & 0 deletions integrations/vue/snap/cjs/App.vue2.cjs
@@ -0,0 +1,13 @@
"use strict";
const vue = require("vue");
const HelloWorld = require("./components/HelloWorld.vue.cjs");
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
__name: "App",
setup(__props) {
return (_ctx, _cache) => {
return vue.openBlock(), vue.createBlock(HelloWorld, { msg: "Vite + Vue" });
};
}
});
module.exports = _sfc_main;
//# sourceMappingURL=App.vue2.cjs.map
1 change: 1 addition & 0 deletions integrations/vue/snap/cjs/App.vue2.cjs.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions integrations/vue/snap/cjs/App.vue3.cjs
@@ -0,0 +1,2 @@
"use strict";
//# sourceMappingURL=App.vue3.cjs.map
1 change: 1 addition & 0 deletions integrations/vue/snap/cjs/App.vue3.cjs.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions integrations/vue/snap/cjs/_virtual/_plugin-vue_export-helper.cjs
@@ -0,0 +1,10 @@
"use strict";
const _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
module.exports = _export_sfc;
//# sourceMappingURL=_plugin-vue_export-helper.cjs.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f3bd6a9

Please sign in to comment.