diff --git a/README.md b/README.md index 0fafafd..662f239 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ interface NuxtRuntimeCompilerOptions { You can specify the node_modules root directory if your `node_modules` directory is not at your `process.cwd()`. -Default value is `./` +- Default value is `./` For example if you are running `nuxt build` from a project in `root/packages/{YOUR_WORKSPACE}` while your `node_modules` is in `root/` then the nodeModulesRoot should be @@ -39,3 +39,27 @@ export default defineNuxtConfig({ }] }) ``` + +## RuntimeCompilerOptions + +See [app.config documentation](https://nuxt.com/docs/examples/app/app-config) +This module overloads the `AppConfig` exported by `app.config.ts` to pass the [RuntimeCompilerOptions](https://github.com/vuejs/core/blob/dbe7109c8f6417770129dc92313f05feac0c0edb/packages/runtime-core/src/componentOptions.ts#L213-L218) to your VueApp in runtime. + +Compatible options from [RuntimeCompilerOptions](https://github.com/vuejs/core/blob/dbe7109c8f6417770129dc92313f05feac0c0edb/packages/runtime-core/src/componentOptions.ts#L213-L218) in `app.config` will also be used by the builder during build time. + +`app.config.ts` + +```ts +export default defineAppConfig({ + vue: { + compilerOptions: { + isCustomElement: (tag) => { + return [ + 'math', + 'maction', + ].includes(tag) + } + } + } +}) +``` \ No newline at end of file diff --git a/package.json b/package.json index 2d41a2c..82cd63a 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,12 @@ "dev": "nuxi dev playground", "dev:build": "nuxi build playground", "dev:prepare": "nuxt-module-build --stub && nuxi prepare playground", - "test": "vitest --dir test", + "test": "yarn nuxi prepare ./test/fixtures/basic && vitest --dir test", "test:dev": "npx cross-env NUXT_TEST_DEV=true yarn test", - "test:webpack": "npx cross-env BUILDER=webpack vitest --dir test", + "test:webpack": "npx cross-env BUILDER=webpack vitest --dir test", "test:external-vue-disabled": "npx cross-env FIXTURE=external-vue-disabled yarn test", "test:external-vue-disabled:dev": "npx cross-env NUXT_TEST_DEV=true FIXTURE=external-vue-disabled yarn test", - "test:external-vue-disabled:webpack": "npx cross-env BUILDER=webpack FIXTURE=external-vue-disabled yarn test", + "test:external-vue-disabled:webpack": "npx cross-env BUILDER=webpack FIXTURE=external-vue-disabled yarn test", "lint": "eslint --ext .vue,.js,.ts ./" }, "dependencies": { diff --git a/playground/app.config.ts b/playground/app.config.ts new file mode 100644 index 0000000..f47cfdd --- /dev/null +++ b/playground/app.config.ts @@ -0,0 +1,94 @@ +export default defineAppConfig({ + vue: { + compilerOptions: { + isCustomElement: (tag) => { + return ['math', + 'maction', + 'maligngroup', + 'malignmark', + 'menclose', + 'merror', + 'mfenced', + 'mfrac', + 'mi', + 'mlongdiv', + 'mmultiscripts', + 'mn', + 'mo', + 'mover', + 'mpadded', + 'mphantom', + 'mroot', + 'mrow', + 'ms', + 'mscarries', + 'mscarry', + 'mscarries', + 'msgroup', + 'mstack', + 'mlongdiv', + 'msline', + 'mstack', + 'mspace', + 'msqrt', + 'msrow', + 'mstack', + 'mstack', + 'mstyle', + 'msub', + 'msup', + 'msubsup', + 'mtable', + 'mtd', + 'mtext', + 'mtr', + 'munder', + 'munderover', + 'semantics', + 'math', + 'mi', + 'mn', + 'mo', + 'ms', + 'mspace', + 'mtext', + 'menclose', + 'merror', + 'mfenced', + 'mfrac', + 'mpadded', + 'mphantom', + 'mroot', + 'mrow', + 'msqrt', + 'mstyle', + 'mmultiscripts', + 'mover', + 'mprescripts', + 'msub', + 'msubsup', + 'msup', + 'munder', + 'munderover', + 'none', + 'maligngroup', + 'malignmark', + 'mtable', + 'mtd', + 'mtr', + 'mlongdiv', + 'mscarries', + 'mscarry', + 'msgroup', + 'msline', + 'msrow', + 'mstack', + 'maction', + 'semantics', + 'annotation', + 'annotation-xml' + ].includes(tag) + } + } + } +}) diff --git a/src/module.ts b/src/module.ts index 909fbbb..4d8fbad 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,5 +1,7 @@ -import { defineNuxtModule, isNuxt2, isNuxt3 } from '@nuxt/kit' -import { resolve } from 'pathe' +import { resolve } from 'path' +import { existsSync } from 'fs' +import { addPlugin, createResolver, defineNuxtModule, isNuxt2, isNuxt3, resolvePath } from '@nuxt/kit' +import type { AppConfig } from '@nuxt/schema' interface NuxtRuntimeCompilerOptions { nodeModulesRoot?: string @@ -10,9 +12,7 @@ export default defineNuxtModule({ name: 'nuxt-runtime-compiler', configKey: 'nuxtRuntimeCompiler' }, - setup (options: NuxtRuntimeCompilerOptions, nuxt) { - const { nodeModulesRoot = './' } = options - + async setup ({ nodeModulesRoot = './' } : NuxtRuntimeCompilerOptions, nuxt) { if (isNuxt2(nuxt)) { /** override all nuxt default vue aliases to force uses of the full bundle of VueJS */ const vueFullCommonPath = 'vue/dist/vue.common.js' @@ -124,6 +124,52 @@ export default defineNuxtModule({ } }) }) + + const resolver = createResolver(import.meta.url) + const runtimeDir = await resolver.resolve('./runtime') + nuxt.options.build.transpile.push(runtimeDir) + + addPlugin(resolve(runtimeDir, 'nuxt-runtime-compiler.plugin.ts')) + + nuxt.hook('prepare:types', ({ references }) => { + references.push({ path: resolve(runtimeDir, 'types.d.ts') }) + }) + + const appConfigPath = await resolvePath('app.config') + + // use AppConfig to define vue compiler options at build time + if (existsSync(appConfigPath)) { + const globalDefineAppConfig = (globalThis as any).defineAppConfig + + if (!globalDefineAppConfig) { + // allow defineAppConfig + (globalThis as any).defineAppConfig = (c: any) => c + } + const appConfig = await import(appConfigPath) as AppConfig + + nuxt.options.vite.vue = { + ...nuxt.options.vite.vue, + template: { + ...nuxt.options.vite.vue?.template, + compilerOptions: { + ...nuxt.options.vite.vue?.template?.compilerOptions, + ...appConfig.vue?.compilerOptions + } + } + } + + nuxt.options.webpack.loaders.vue = { + ...nuxt.options.webpack.loaders.vue, + compilerOptions: { + ...nuxt.options.webpack.loaders.vue.compilerOptions, + ...appConfig.vue?.compilerOptions + } + } + + if (!globalDefineAppConfig) { + delete (globalThis as any).defineAppConfig + } + } } } }) diff --git a/src/runtime/nuxt-runtime-compiler.plugin.ts b/src/runtime/nuxt-runtime-compiler.plugin.ts new file mode 100644 index 0000000..818cfa9 --- /dev/null +++ b/src/runtime/nuxt-runtime-compiler.plugin.ts @@ -0,0 +1,6 @@ +import { defineNuxtPlugin, useAppConfig } from '#imports' + +export default defineNuxtPlugin((nuxtApp) => { + const appConfig = useAppConfig() + nuxtApp.vueApp.config.compilerOptions.isCustomElement = appConfig.vue?.compilerOptions?.isCustomElement +}) diff --git a/src/runtime/types.d.ts b/src/runtime/types.d.ts new file mode 100644 index 0000000..3fb663c --- /dev/null +++ b/src/runtime/types.d.ts @@ -0,0 +1,16 @@ + +import { RuntimeCompilerOptions } from 'vue' + +declare module '@nuxt/schema' { + interface AppConfig { + vue?: { + compilerOptions?: RuntimeCompilerOptions + } + } + + interface AppConfigInput { + vue?: { + compilerOptions?: RuntimeCompilerOptions + } + } +} diff --git a/test/fixtures/basic/app.config.ts b/test/fixtures/basic/app.config.ts new file mode 100644 index 0000000..0becd28 --- /dev/null +++ b/test/fixtures/basic/app.config.ts @@ -0,0 +1,95 @@ +export default defineAppConfig({ + vue: { + compilerOptions: { + isCustomElement: (tag) => { + return [ + 'math', + 'maction', + 'maligngroup', + 'malignmark', + 'menclose', + 'merror', + 'mfenced', + 'mfrac', + 'mi', + 'mlongdiv', + 'mmultiscripts', + 'mn', + 'mo', + 'mover', + 'mpadded', + 'mphantom', + 'mroot', + 'mrow', + 'ms', + 'mscarries', + 'mscarry', + 'mscarries', + 'msgroup', + 'mstack', + 'mlongdiv', + 'msline', + 'mstack', + 'mspace', + 'msqrt', + 'msrow', + 'mstack', + 'mstack', + 'mstyle', + 'msub', + 'msup', + 'msubsup', + 'mtable', + 'mtd', + 'mtext', + 'mtr', + 'munder', + 'munderover', + 'semantics', + 'math', + 'mi', + 'mn', + 'mo', + 'ms', + 'mspace', + 'mtext', + 'menclose', + 'merror', + 'mfenced', + 'mfrac', + 'mpadded', + 'mphantom', + 'mroot', + 'mrow', + 'msqrt', + 'mstyle', + 'mmultiscripts', + 'mover', + 'mprescripts', + 'msub', + 'msubsup', + 'msup', + 'munder', + 'munderover', + 'none', + 'maligngroup', + 'malignmark', + 'mtable', + 'mtd', + 'mtr', + 'mlongdiv', + 'mscarries', + 'mscarry', + 'msgroup', + 'msline', + 'msrow', + 'mstack', + 'maction', + 'semantics', + 'annotation', + 'annotation-xml' + ].includes(tag) + } + } + } +}) diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts index cca2e73..cc65f6c 100644 --- a/test/fixtures/basic/nuxt.config.ts +++ b/test/fixtures/basic/nuxt.config.ts @@ -1,4 +1,3 @@ -import { defineNuxtConfig } from 'nuxt/config' import nuxtRuntimeCompiler from '../../../src/module' export default defineNuxtConfig({ diff --git a/test/fixtures/basic/pages/index.vue b/test/fixtures/basic/pages/index.vue index 865a88e..084627c 100644 --- a/test/fixtures/basic/pages/index.vue +++ b/test/fixtures/basic/pages/index.vue @@ -41,11 +41,45 @@ const Interactive = h({ }, props: data.value?.interactiveComponent.props }) as Component + +const CustomElementComponent = defineComponent({ + template: ` + + + f + ( + x + ) + = + + + ∞ + + + ∞ + + + f^ + + ( + ξ + ) + e2πiξxdξ + + % \\f is defined as #1f(#2) using the macro + \\f\\relax{x} = \\int_{-\\infty}^\\infty + \\f\\hat\\xi\\,e^{2 \\pi i \\xi x} + \\,d\\xi + + + ` +}) + + + + f + ( + x + ) + = + + + ∞ + + + ∞ + + + f^ + + ( + ξ + ) + e2πiξxdξ + + % \\f is defined as #1f(#2) using the macro + \\f\\relax{x} = \\int_{-\\infty}^\\infty + \\f\\hat\\xi\\,e^{2 \\pi i \\xi x} + \\,d\\xi + + + diff --git a/tsconfig.json b/tsconfig.json index 62575cf..0d4eaf3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,9 @@ "resolveJsonModule": true, "types": [ "node" - ] + ], + "paths": { + "#imports": ["./node_modules/nuxt/app.d.ts"], + } } } \ No newline at end of file