Skip to content

Commit

Permalink
feat(vite): automatically update tsconfig paths for chosen types
Browse files Browse the repository at this point in the history
  • Loading branch information
harlan-zw committed Aug 24, 2022
1 parent 5cf9c96 commit b6edee9
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 133 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"eslint": "^8.22.0",
"jsdom": "^20.0.0",
"nuxt": "3.0.0-rc.8",
"playwright": "^1.25.0",
"playwright": "^1.25.1",
"typescript": "^4.7.4",
"unbuild": "^0.8.9",
"utility-types": "^3.10.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"@vueuse/schema-org": "workspace:*",
"@vueuse/schema-org-vite": "workspace:*",
"pathe": "^0.3.5",
"schema-org-graph-js": "0.4.4"
"schema-org-graph-js": "0.4.5"
},
"devDependencies": {
"@nuxt/module-builder": "latest",
Expand Down
83 changes: 46 additions & 37 deletions packages/schema-org/providers/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,53 +69,62 @@ import {
} from 'schema-org-graph-js'
import type { Ref } from 'vue'

type MaybeRef<T> = {
[P in keyof T]?: T[P] | Ref<T[P]>;
}
export type MaybeRef<T> = T | Ref<T>

export type DeepMaybeRef<T> = T extends Ref<infer V>
? MaybeRef<V>
: T extends Array<any> | object
? { [K in keyof T]: DeepMaybeRef<T[K]> }
: MaybeRef<T>

type WithResolver<T> = T & {
type Node<T> = Omit<DeepMaybeRef<T>, '@type'>

type MaybeWithResolver<T> = T & {
_resolver?: any
_uid?: number
}

const provideResolver = <T>(input?: T, resolver?: any) => {
return <WithResolver<T>> {
return <MaybeWithResolver<T>> {
...(input || {}),
_resolver: resolver,
}
}

export const defineAddress = <T extends MaybeRef<PostalAddress>>(input?: T) => provideResolver(input, addressResolver)
export const defineAggregateOffer = <T extends MaybeRef<AggregateOffer>>(input?: T) => provideResolver(input, aggregateOfferResolver)
export const defineAggregateRating = <T extends MaybeRef<AggregateRating>>(input?: T) => provideResolver(input, aggregateRatingResolver)
export const defineArticle = <T extends MaybeRef<Article>>(input?: T) => provideResolver(input, articleResolver)
export const defineBreadcrumb = <T extends MaybeRef<BreadcrumbList>>(input?: T) => provideResolver(input, breadcrumbResolver)
export const defineComment = <T extends MaybeRef<Comment>>(input?: T) => provideResolver(input, commentResolver)
export const defineEvent = <T extends MaybeRef<Event>>(input?: T) => provideResolver(input, eventResolver)
export const defineVirtualLocation = <T extends MaybeRef<VirtualLocation>>(input?: T) => provideResolver(input, virtualLocationResolver)
export const definePlace = <T extends MaybeRef<Place>>(input?: T) => provideResolver(input, placeResolver)
export const defineHowTo = <T extends MaybeRef<HowTo>>(input?: T) => provideResolver(input, howToResolver)
export const defineHowToStep = <T extends MaybeRef<HowToStep>>(input?: T) => provideResolver(input, howToStepResolver)
export const defineImage = <T extends MaybeRef<ImageObject>>(input?: T) => provideResolver(input, imageResolver)
export const defineLocalBusiness = <T extends MaybeRef<LocalBusiness>>(input?: T) => provideResolver(input, localBusinessResolver)
export const defineOffer = <T extends MaybeRef<Offer>>(input?: T) => provideResolver(input, offerResolver)
export const defineOpeningHours = <T extends MaybeRef<OpeningHoursSpecification>>(input?: T) => provideResolver(input, resolveOpeningHours)
export const defineOrganization = <T extends MaybeRef<Organization>>(input?: T) => provideResolver(input, organizationResolver)
export const definePerson = <T extends MaybeRef<Person>>(input?: T) => provideResolver(input, personResolver)
export const defineProduct = <T extends MaybeRef<Product>>(input?: T) => provideResolver(input, productResolver)
export const defineQuestion = <T extends MaybeRef<Question>>(input?: T) => provideResolver(input, questionResolver)
export const defineRecipe = <T extends MaybeRef<Recipe>>(input?: T) => provideResolver(input, recipeResolver)
export const defineReview = <T extends MaybeRef<Review>>(input?: T) => provideResolver(input, reviewResolver)
export const defineVideo = <T extends MaybeRef<VideoObject>>(input?: T) => provideResolver(input, videoResolver)
export const defineWebPage = <T extends MaybeRef<WebPage>>(input?: T) => provideResolver(input, webPageResolver)
export const defineWebSite = <T extends MaybeRef<WebSite>>(input?: T) => provideResolver(input, webSiteResolver)
export const defineBook = <T extends MaybeRef<Book>>(input?: T) => provideResolver(input, bookResolver)
export const defineBookEdition = <T extends MaybeRef<BookEdition>>(input?: T) => provideResolver(input, bookEditionResolver)
export const defineCourse = <T extends MaybeRef<Course>>(input?: T) => provideResolver(input, courseResolver)
export const defineItemList = <T extends MaybeRef<ItemList>>(input?: T) => provideResolver(input, itemListResolver)
export const defineMovie = <T extends MaybeRef<Movie>>(input?: T) => provideResolver(input, movieResolver)
export const defineSoftwareApp = <T extends MaybeRef<SoftwareApp>>(input?: T) => provideResolver(input, softwareAppResolver)
export const defineSearchAction = <T extends MaybeRef<SearchAction>>(input?: T) => provideResolver(input, searchActionResolver)
export const defineReadAction = <T extends MaybeRef<ReadAction>>(input?: T) => provideResolver(input, readActionResolver)
export const defineAddress = <T extends Node<PostalAddress>>(input?: T) => provideResolver(input, addressResolver)
export const defineAggregateOffer = <T extends Node<AggregateOffer>>(input?: T) => provideResolver(input, aggregateOfferResolver)
export const defineAggregateRating = <T extends Node<AggregateRating>>(input?: T) => provideResolver(input, aggregateRatingResolver)
export const defineArticle = <T extends Node<Article>>(input?: T) => provideResolver(input, articleResolver)
export const defineBreadcrumb = <T extends Node<BreadcrumbList>>(input?: T) => provideResolver(input, breadcrumbResolver)
export const defineComment = <T extends Node<Comment>>(input?: T) => provideResolver(input, commentResolver)
export const defineEvent = <T extends Node<Event>>(input?: T) => provideResolver(input, eventResolver)
export const defineVirtualLocation = <T extends Node<VirtualLocation>>(input?: T) => provideResolver(input, virtualLocationResolver)
export const definePlace = <T extends Node<Place>>(input?: T) => provideResolver(input, placeResolver)
export const defineHowTo = <T extends Node<HowTo>>(input?: T) => provideResolver(input, howToResolver)
export const defineHowToStep = <T extends Node<HowToStep>>(input?: T) => provideResolver(input, howToStepResolver)
export const defineImage = <T extends Node<ImageObject>>(input?: T) => provideResolver(input, imageResolver)
export const defineLocalBusiness = <T extends Node<LocalBusiness>>(input?: T) => provideResolver(input, localBusinessResolver)
export const defineOffer = <T extends Node<Offer>>(input?: T) => provideResolver(input, offerResolver)
export const defineOpeningHours = <T extends Node<OpeningHoursSpecification>>(input?: T) => provideResolver(input, resolveOpeningHours)
export const defineOrganization = <T extends Node<Organization>>(input?: T) => provideResolver(input, organizationResolver)
export const definePerson = <T extends Node<Person>>(input?: T) => provideResolver(input, personResolver)
export const defineProduct = <T extends Node<Product>>(input?: T) => provideResolver(input, productResolver)
export const defineQuestion = <T extends Node<Question>>(input?: T) => provideResolver(input, questionResolver)
export const defineRecipe = <T extends Node<Recipe>>(input?: T) => provideResolver(input, recipeResolver)
export const defineReview = <T extends Node<Review>>(input?: T) => provideResolver(input, reviewResolver)
export const defineVideo = <T extends Node<VideoObject>>(input?: T) => provideResolver(input, videoResolver)
export const defineWebPage = <T extends Node<WebPage>>(input?: T) => provideResolver(input, webPageResolver)
export const defineWebSite = <T extends Node<WebSite>>(input?: T) => provideResolver(input, webSiteResolver)
export const defineBook = <T extends Node<Book>>(input?: T) => provideResolver(input, bookResolver)
export const defineCourse = <T extends Node<Course>>(input?: T) => provideResolver(input, courseResolver)
export const defineItemList = <T extends Node<ItemList>>(input?: T) => provideResolver(input, itemListResolver)
export const defineMovie = <T extends Node<Movie>>(input?: T) => provideResolver(input, movieResolver)
export const defineSearchAction = <T extends Node<SearchAction>>(input?: T) => provideResolver(input, searchActionResolver)
export const defineReadAction = <T extends Node<ReadAction>>(input?: T) => provideResolver(input, readActionResolver)

/* simple-only */
export const defineSoftwareApp = <T extends Node<SoftwareApp>>(input?: T) => provideResolver(input, softwareAppResolver)
export const defineBookEdition = <T extends Node<BookEdition>>(input?: T) => provideResolver(input, bookEditionResolver)
/* end-simple-only */

export { Thing }
11 changes: 6 additions & 5 deletions packages/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,17 @@
"dependencies": {
"@vueuse/schema-org": "workspace:*",
"magic-string": "^0.26.2",
"mlly": "^0.5.12",
"pathe": "^0.3.4",
"mlly": "^0.5.14",
"pathe": "^0.3.5",
"schema-dts": "^1.1.0",
"unplugin": "^0.9.2"
"unplugin": "^0.9.4",
"pkg-types": "^0.3.4"
},
"devDependencies": {
"unplugin-vue-components": "^0.22.4",
"vite": "^3.0.8",
"vite": "^3.0.9",
"vite-ssg": "^0.20.2",
"vitepress": "1.0.0-alpha.8",
"vitepress": "1.0.0-alpha.10",
"vue-router": "^4.1.4"
}
}
64 changes: 50 additions & 14 deletions packages/vite/src/plugins/alias.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { createUnplugin } from 'unplugin'
import MagicString from 'magic-string'
import { resolvePath } from 'mlly'
import { dirname } from 'pathe'
import { dirname, relative } from 'pathe'
import { AliasProvider, AliasRuntime, PkgName } from '@vueuse/schema-org'
import { readTSConfig, resolveTSConfig, writeTSConfig } from 'pkg-types'

export interface AliasPluginOptions {
/**
* Whether the tsconfig.json should be updated with the aliases.
*/
dts?: boolean
/**
* Should the runtime be swapped out with a mock one, used for SSR-only mode.
*/
Expand Down Expand Up @@ -33,10 +38,11 @@ interface AliasPaths {

export const AliasRuntimePlugin = () => createUnplugin<AliasPluginOptions>((userConfig) => {
userConfig = userConfig || {}
let paths: AliasPaths
const fetchPaths = async (force = false) => {
if (paths && !force)
return paths
let cachedPaths: AliasPaths
let updatedTSConfig = false
const fetchPaths = async (ctx: { root?: string }, force = false) => {
if (cachedPaths && !force)
return cachedPaths
const pkg = userConfig.paths?.pkg || dirname(await resolvePath(PkgName))
let provider, runtime
if (userConfig?.mock) {
Expand All @@ -46,29 +52,58 @@ export const AliasRuntimePlugin = () => createUnplugin<AliasPluginOptions>((user
else {
provider = userConfig.paths?.provider || `${pkg}/providers/${userConfig?.full ? 'full' : 'simple'}`
runtime = userConfig.paths?.runtime || `${pkg}/runtime`

// update types for whichever provider we're using
if (!updatedTSConfig && userConfig.dts && ctx.root && process.env.NODE_ENV !== 'production') {
const tsConfigFile = await resolveTSConfig(ctx.root)
if (tsConfigFile) {
const tsconfig = await readTSConfig(tsConfigFile)
tsconfig.compilerOptions = tsconfig.compilerOptions || {}
tsconfig.compilerOptions.paths = tsconfig.compilerOptions.paths || []
const providerTsPath = relative(ctx.root, provider)
if (tsconfig.compilerOptions.paths[AliasProvider]?.[0] !== providerTsPath) {
tsconfig.compilerOptions.paths[AliasProvider] = [providerTsPath]
updatedTSConfig = true
}

const runtimeTsPath = relative(ctx.root, `${runtime}/index`)
if (tsconfig.compilerOptions.paths[AliasRuntime]?.[0] !== runtimeTsPath) {
tsconfig.compilerOptions.paths[AliasRuntime] = [runtimeTsPath]
updatedTSConfig = true
}

if (updatedTSConfig)
await writeTSConfig(tsConfigFile, tsconfig)
// avoid checking again this run
updatedTSConfig = true
}
}
}
paths = {

const resolvedPaths = {
pkg,
provider,
runtime,
}
return paths
if (ctx.root)
cachedPaths = resolvedPaths
return resolvedPaths
}

return {
name: '@vueuse/schema-org:aliases',
enforce: 'pre',
async buildStart() {
await fetchPaths()
await fetchPaths({})
},
transformInclude(id) {
return id.includes(paths.pkg)
return id.includes(cachedPaths.pkg)
},
transform(code) {
// swap out aliases for real paths
const s = new MagicString(code)
s.replace(AliasProvider, paths.provider)
s.replace(AliasRuntime, paths.runtime)
s.replace(AliasProvider, cachedPaths.provider)
s.replace(AliasRuntime, cachedPaths.runtime)

if (s.hasChanged()) {
return {
Expand All @@ -78,7 +113,7 @@ export const AliasRuntimePlugin = () => createUnplugin<AliasPluginOptions>((user
}
},
async webpack(compiler) {
const { provider, runtime } = await fetchPaths()
const { provider, runtime } = await fetchPaths({ root: compiler.context })

compiler.options.resolve.alias = {
...compiler.options.resolve.alias || {},
Expand All @@ -88,14 +123,15 @@ export const AliasRuntimePlugin = () => createUnplugin<AliasPluginOptions>((user
},
vite: {
async config(config, ctx) {
const root = config.root || process.cwd()
let paths
const isServerBuild = process.env.VITE_SSG || ctx.ssrBuild
if (typeof userConfig.mock === 'undefined' && !isServerBuild && config.mode === 'production') {
userConfig.mock = true
paths = await fetchPaths(true)
paths = await fetchPaths({ root }, true)
}
else {
paths = await fetchPaths()
paths = await fetchPaths({ root })
}

const { pkg, provider, runtime } = paths
Expand Down
1 change: 0 additions & 1 deletion playgrounds/vite/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"#vueuse/schema-org/*": ["@vueuse/schema-org/*"]
}
},

Expand Down
9 changes: 6 additions & 3 deletions playgrounds/vitesse/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ useHead({
useSchemaOrg([
defineWebSite({
name: 'Vitesse',
name: computed(() => {
return 'test'
}),
}),
defineOrganization({
name: 'Vitesse',
logo: '/logo.png',
'@type': 'Organization',
'name': 'Vitesse',
'logo': '/logo.png',
}),
defineWebPage(),
])
Expand Down
23 changes: 19 additions & 4 deletions playgrounds/vitesse/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
"baseUrl": ".",
"module": "ESNext",
"target": "ESNext",
"lib": ["DOM", "ESNext"],
"lib": [
"DOM",
"ESNext"
],
"strict": true,
"esModuleInterop": true,
"jsx": "preserve",
Expand All @@ -22,8 +25,20 @@
"vite-plugin-vue-layouts/client"
],
"paths": {
"~/*": ["src/*"]
"~/*": [
"src/*"
],
"#vueuse/schema-org/provider": [
"../../packages/schema-org/dist/providers/simple"
],
"#vueuse/schema-org/runtime": [
"../../packages/schema-org/dist/runtime/index"
]
}
},
"exclude": ["dist", "node_modules", "cypress"]
}
"exclude": [
"dist",
"node_modules",
"cypress"
]
}
10 changes: 8 additions & 2 deletions playgrounds/vitesse/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import path from 'path'
import path, {resolve} from 'path'
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import Pages from 'vite-plugin-pages'
Expand All @@ -13,11 +13,13 @@ import Inspect from 'vite-plugin-inspect'
import LinkAttributes from 'markdown-it-link-attributes'
import Unocss from 'unocss/vite'
import Shiki from 'markdown-it-shiki'
import { AliasRuntimePluginVite as SchemaOrg, SchemaOrgResolver, schemaOrgAutoImports } from '@vueuse/schema-org-vite'
import { SchemaOrg, SchemaOrgResolver, schemaOrgAutoImports } from '@vueuse/schema-org-vite'

export default defineConfig({
resolve: {
alias: {
'@vueuse/schema-org': resolve(__dirname, '../../packages/schema-org/dist'),
'@vueuse/schema-org-vite': resolve(__dirname, '../../packages/vite/dist'),
'~/': `${path.resolve(__dirname, 'src')}/`,
},
},
Expand All @@ -37,6 +39,10 @@ export default defineConfig({
Layouts(),

SchemaOrg({
// simple types
full: false,
// write alias changes to tsconfig.json
dts: true,
}),

// https://github.com/antfu/unplugin-auto-import
Expand Down

0 comments on commit b6edee9

Please sign in to comment.