├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .node-version ├── .prettierrc ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example ├── App.vue ├── Button.vue ├── ScriptSetup.vue ├── TypeScript.vue ├── devServer.js ├── docs-loader.js ├── index.html ├── logo.png ├── main.js ├── ssr.js ├── types.ts └── webpack.config.js ├── jest.config.js ├── package.json ├── pnpm-lock.yaml ├── src ├── compiler.ts ├── cssModules.ts ├── descriptorCache.ts ├── exportHelper.ts ├── formatError.ts ├── hotReload.ts ├── index.ts ├── pitcher.ts ├── plugin.ts ├── pluginWebpack4.ts ├── pluginWebpack5.ts ├── resolveScript.ts ├── select.ts ├── shim.d.ts ├── styleInlineLoader.ts ├── stylePostLoader.ts ├── templateLoader.ts └── util.ts ├── test ├── advanced.spec.ts ├── core.spec.ts ├── custom.spec.ts ├── edgeCases.spec.ts ├── fixtures │ ├── App.ts │ ├── ScriptSetup.vue │ ├── basic.vue │ ├── css-modules-extend.vue │ ├── css-modules-simple.vue │ ├── css-modules.vue │ ├── custom-import.vue │ ├── custom-language.vue │ ├── custom-query.vue │ ├── duplicate-cssm.css │ ├── duplicate-cssm.js │ ├── duplicate-cssm.vue │ ├── empty-style.vue │ ├── entry.js │ ├── extract-css-chunks.vue │ ├── extract-css.vue │ ├── i18n-entry.js │ ├── i18n.vue │ ├── imported-types-aliased.ts │ ├── imported-types.ts │ ├── imported-types.vue │ ├── index.html │ ├── logo.png │ ├── markdown.vue │ ├── named-exports.vue │ ├── no-script.vue │ ├── optional-chaining.vue │ ├── postcss.vue │ ├── pre.vue │ ├── process-custom-file │ │ ├── custom-file.svg │ │ └── process-custom-file.vue │ ├── resolve.vue │ ├── scoped-css.vue │ ├── script-import.js │ ├── script-import.vue │ ├── style-import-scoped.css │ ├── style-import-twice-sub.vue │ ├── style-import-twice.vue │ ├── style-import.css │ ├── style-import.vue │ ├── style-v-bind.vue │ ├── template-import-pre.vue │ ├── template-import.html │ ├── template-import.pug │ ├── template-import.vue │ ├── ts.vue │ ├── tsconfig.json │ └── unit-test.js ├── mock-loaders │ ├── docs.js │ ├── html.js │ ├── js.js │ └── query.js ├── script.spec.ts ├── style.spec.ts ├── template.spec.ts └── utils.ts └── tsconfig.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: 'ci' 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | test-webpack4: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: pnpm/action-setup@v2 15 | - uses: actions/setup-node@v3 16 | with: 17 | node-version: '16' 18 | cache: 'pnpm' 19 | - run: pnpm install 20 | - run: pnpm pretest:webpack4 21 | - run: pnpm test:webpack4 22 | 23 | test-webpack5: 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | - uses: pnpm/action-setup@v2 28 | - uses: actions/setup-node@v3 29 | with: 30 | node-version: '16' 31 | cache: 'pnpm' 32 | - run: pnpm install 33 | - run: pnpm pretest 34 | - run: pnpm test 35 | 36 | test-webpack5-inline-match-resource: 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v3 40 | - uses: pnpm/action-setup@v2 41 | - uses: actions/setup-node@v3 42 | with: 43 | node-version: '16' 44 | cache: 'pnpm' 45 | - run: pnpm install 46 | - run: pnpm pretest:match-resource 47 | - run: pnpm test:match-resource 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist 5 | dist-ssr 6 | link.sh 7 | .cache 8 | TODOs.md 9 | coverage 10 | .vscode 11 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | v16.20.0 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | semi: false 2 | singleQuote: true 3 | printWidth: 80 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [17.4.2](https://github.com/vuejs/vue-loader/compare/v17.4.1...v17.4.2) (2023-12-30) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * pass compilerOptions to sfc parse & re-enable AST reuse ([d2a2e05](https://github.com/vuejs/vue-loader/commit/d2a2e051c3d985d1ae6bb468749b24543631b482)) 7 | 8 | 9 | 10 | ## [17.4.1](https://github.com/vuejs/vue-loader/compare/v17.4.0...v17.4.1) (2023-12-30) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * (temporarily) disable template ast reuse ([31b03af](https://github.com/vuejs/vue-loader/commit/31b03af121edbe70337f538b1def95acbef4d0f1)) 16 | 17 | 18 | 19 | # [17.4.0](https://github.com/vuejs/vue-loader/compare/v17.3.1...v17.4.0) (2023-12-25) 20 | 21 | 22 | ### Features 23 | 24 | * leverage ast reuse in 3.4 ([479835f](https://github.com/vuejs/vue-loader/commit/479835fe751691a39c62cda12bffeef9e6830443)) 25 | 26 | 27 | 28 | ## [17.3.1](https://github.com/vuejs/vue-loader/compare/v17.3.0...v17.3.1) (2023-10-31) 29 | 30 | 31 | ### Bug Fixes 32 | 33 | * do not skip style post loader for v-bind() in CSS ([d7071bb](https://github.com/vuejs/vue-loader/commit/d7071bbdeb45518c053bdae8eb7de52fc598adc6)), closes [#2061](https://github.com/vuejs/vue-loader/issues/2061) 34 | 35 | 36 | # [17.3.0](https://github.com/vuejs/vue-loader/compare/v17.2.2...v17.3.0) (2023-10-07) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * re-use ident of vue rule for template compiler, fixes [#2029](https://github.com/vuejs/vue-loader/issues/2029) ([#2030](https://github.com/vuejs/vue-loader/issues/2030)) ([b50fa56](https://github.com/vuejs/vue-loader/commit/b50fa5665f2cbc1c0b8e18f65dd3adf1dfe848dc)) 42 | 43 | 44 | ### Features 45 | 46 | * skip normal css files without scoped flag in stylePostLoader ([#2053](https://github.com/vuejs/vue-loader/issues/2053)) ([98782e7](https://github.com/vuejs/vue-loader/commit/98782e794fadca83b600b047b55aa6f0c230cc25)) 47 | 48 | 49 | 50 | ## [17.2.2](https://github.com/vuejs/vue-loader/compare/v17.2.1...v17.2.2) (2023-06-02) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * windows path for `experiments.css` ([#2049](https://github.com/vuejs/vue-loader/issues/2049)) ([f3f45df](https://github.com/vuejs/vue-loader/commit/f3f45df925bcd80307232e7410ead355f87953d3)) 56 | 57 | 58 | 59 | ## [17.2.1](https://github.com/vuejs/vue-loader/compare/v17.1.2...v17.2.1) (2023-06-01) 60 | 61 | 62 | ### Features 63 | 64 | * A new `experimentalInlineMatchResource` option (webpack 5 only), which leverages webpack 5's inline match resource feature and works well with the [`experiments.css`](https://webpack.js.org/configuration/experiments/#experimentscss) feature ([#2046](https://github.com/vuejs/vue-loader/issues/2046)) ([3149f6d](https://github.com/vuejs/vue-loader/commit/3149f6d69c2f456bdcfa23acc0da93473a93a764)) 65 | 66 | 67 | Note: v17.2.0 was released by accident, it has the same content as v17.1.2, therefore not included in the changelog. 68 | 69 | 70 | ## [17.1.2](https://github.com/vuejs/vue-loader/compare/v17.1.1...v17.1.2) (2023-05-29) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * keep build stable when run in a different path ([#2040](https://github.com/vuejs/vue-loader/issues/2040)) ([a81dc0f](https://github.com/vuejs/vue-loader/commit/a81dc0f573d216eefa13b0275f3fc147bf3e2ef3)) 76 | * properly close the watcher after tests ([40b93b9](https://github.com/vuejs/vue-loader/commit/40b93b9c2722e85a000de62e3eb8bc02facafd10)) 77 | 78 | 79 | 80 | ## [17.1.1](https://github.com/vuejs/vue-loader/compare/v17.1.0...v17.1.1) (2023-05-11) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * support propsDestructure and defineModel options ([6269698](https://github.com/vuejs/vue-loader/commit/6269698f9fda37f0e3849db3e8b8e67ad1862f57)) 86 | 87 | 88 | 89 | # [17.1.0](https://github.com/vuejs/vue-loader/compare/v17.1.0-beta.0...v17.1.0) (2023-04-26) 90 | 91 | 92 | ### Bug Fixes 93 | 94 | * do not throw when `Rule.layer` ([#2000](https://github.com/vuejs/vue-loader/issues/2000)) ([ef589df](https://github.com/vuejs/vue-loader/commit/ef589df2956506a5a7bbc050c476501d32dd8469)) 95 | 96 | 97 | 98 | # [17.1.0-beta.0](https://github.com/vuejs/vue-loader/compare/v17.0.1...v17.1.0-beta.0) (2023-04-19) 99 | 100 | 101 | ### Bug Fixes 102 | 103 | * reference project compiler, fixes [#2031](https://github.com/vuejs/vue-loader/issues/2031) ([#2038](https://github.com/vuejs/vue-loader/issues/2038)) ([cc6fa9e](https://github.com/vuejs/vue-loader/commit/cc6fa9ebf85972a08fc8bbc359b0093b15790745)) 104 | 105 | 106 | ### Features 107 | 108 | * support 3.3 imported types hmr ([bbd98fc](https://github.com/vuejs/vue-loader/commit/bbd98fc8bdc17fcbffb456a5ffe772bc184f22e4)) 109 | 110 | 111 | 112 | ## [17.0.1](https://github.com/vuejs/vue-loader/compare/v17.0.0...v17.0.1) (2022-10-28) 113 | 114 | 115 | ### Bug Fixes 116 | 117 | * add `vue` and `@vue/compiler-sfc` to optional peerDependencies ([df0ded5](https://github.com/vuejs/vue-loader/commit/df0ded5356864b9923da8f89ff33db1ae6c2402f)), closes [#1944](https://github.com/vuejs/vue-loader/issues/1944) 118 | * merge custom queries rather than appending ([#1911](https://github.com/vuejs/vue-loader/issues/1911)) ([9e4249a](https://github.com/vuejs/vue-loader/commit/9e4249a548ceb04ead46fff9b68e9b2676b4c692)) 119 | 120 | 121 | 122 | ## [17.0.1](https://github.com/vuejs/vue-loader/compare/v16.8.3...v17.0.1) (2022-10-28) 123 | 124 | 125 | ### Bug Fixes 126 | 127 | * add `vue` and `@vue/compiler-sfc` to optional peerDependencies ([df0ded5](https://github.com/vuejs/vue-loader/commit/df0ded5356864b9923da8f89ff33db1ae6c2402f)), closes [#1944](https://github.com/vuejs/vue-loader/issues/1944) 128 | * merge custom queries rather than appending ([#1911](https://github.com/vuejs/vue-loader/issues/1911)) ([9e4249a](https://github.com/vuejs/vue-loader/commit/9e4249a548ceb04ead46fff9b68e9b2676b4c692)) 129 | 130 | 131 | 132 | # [17.0.0](https://github.com/vuejs/vue-loader/compare/v16.8.3...v17.0.0) (2021-12-12) 133 | 134 | 135 | ### Features 136 | 137 | * support `reactivityTransform` option ([e07490e](https://github.com/vuejs/vue-loader/commit/e07490ec8b8ac9e00050251d6f0e697fb1f3bf3c)) 138 | 139 | 140 | ### BREAKING CHANGES 141 | 142 | * remove `refSugar` option, require `vue@^3.2.13` 143 | 144 | 145 | 146 | ## [16.8.3](https://github.com/vuejs/vue-loader/compare/v16.8.2...v16.8.3) (2021-11-04) 147 | 148 | 149 | ### Bug Fixes 150 | 151 | * HMR not working correctly with vue-class-component components ([#1897](https://github.com/vuejs/vue-loader/issues/1897)) ([76b1448](https://github.com/vuejs/vue-loader/commit/76b1448eb227c42e1791a691a86782b7a8cacfc0)) 152 | 153 | 154 | 155 | ## [16.8.3](https://github.com/vuejs/vue-loader/compare/v16.8.2...v16.8.3) (2021-11-04) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * HMR not working correctly with vue-class-component components ([#1897](https://github.com/vuejs/vue-loader/issues/1897)) ([76b1448](https://github.com/vuejs/vue-loader/commit/76b1448eb227c42e1791a691a86782b7a8cacfc0)) 161 | 162 | 163 | 164 | ## [16.8.2](https://github.com/vuejs/vue-loader/compare/v16.8.1...v16.8.2) (2021-10-26) 165 | 166 | 167 | ### Bug Fixes 168 | 169 | * should allow chaining with loaders for non-vue files ([#1889](https://github.com/vuejs/vue-loader/issues/1889)) ([f32f953](https://github.com/vuejs/vue-loader/commit/f32f9538ea34fc08e1a28622227896241847690f)), closes [#1879](https://github.com/vuejs/vue-loader/issues/1879) [#1883](https://github.com/vuejs/vue-loader/issues/1883) [#1890](https://github.com/vuejs/vue-loader/issues/1890) 170 | * **plugin:** use compiler.webpack when possible ([#1884](https://github.com/vuejs/vue-loader/issues/1884)) ([820d23c](https://github.com/vuejs/vue-loader/commit/820d23cbf16013dae894e0d84ed9da6e58a37584)) 171 | 172 | 173 | 174 | ## [16.8.1](https://github.com/vuejs/vue-loader/compare/v16.8.0...v16.8.1) (2021-09-22) 175 | 176 | 177 | ### Bug Fixes 178 | 179 | * fix template options resolving for ts ([91f581b](https://github.com/vuejs/vue-loader/commit/91f581b99644119b68e586a0b642fff3811c8741)) 180 | 181 | # [16.8.0](https://github.com/vuejs/vue-loader/compare/v16.7.1...v16.8.0) (2021-09-22) 182 | 183 | 184 | ### Bug Fixes 185 | 186 | * **hmr:** fix hmr regression ([bacc6a9](https://github.com/vuejs/vue-loader/commit/bacc6a9eeca40d6028a2d9a5f6ee02e6c8574abd)) 187 | 188 | 189 | ### Features 190 | 191 | * enableTsInTemplate option ([7613534](https://github.com/vuejs/vue-loader/commit/7613534954b83489a060860b9525a0d121023c5b)) 192 | 193 | - When used with `ts-loader`, due to `ts-loader`'s cache invalidation behavior, it sometimes prevents the template from being hot-reloaded in isolation, causing the component to reload despite only the template being edited. If this is annoying, you can set this option to `false` (and avoid using TS expressions in templates). 194 | 195 | - Alternatively, leave this option on (by default) and use [`esbuild-loader`](https://github.com/privatenumber/esbuild-loader) to transpile TS instead, which doesn't suffer from this problem (it's also a lot faster). However, do note you will need to rely on TS type checking from other sources (e.g. IDE or `vue-tsc`). 196 | 197 | ## [16.7.1](https://github.com/vuejs/vue-loader/compare/v16.7.0...v16.7.1) (2021-09-22) 198 | 199 | 200 | ### Bug Fixes 201 | 202 | * remove pure annotation for custom blocks ([cd891e5](https://github.com/vuejs/vue-loader/commit/cd891e593bf7f8aff852f1d47fda2337de661bea)) 203 | 204 | 205 | # [16.7.0](https://github.com/vuejs/vue-loader/compare/v16.6.0...v16.7.0) (2021-09-21) 206 | 207 | 208 | ### Features 209 | 210 | * support optional @vue/compiler-sfc peer dep ([21725a4](https://github.com/vuejs/vue-loader/commit/21725a4ebc9c8d7f8a590d700017759327e21c2e)) 211 | 212 | 213 | # [16.6.0](https://github.com/vuejs/vue-loader/compare/v16.5.0...v16.6.0) (2021-09-20) 214 | 215 | 216 | ### Bug Fixes 217 | 218 | * generate treeshaking friendly code ([11e3cb8](https://github.com/vuejs/vue-loader/commit/11e3cb8a8a4a4e0aedc2978ce6d7e549a61de3d7)) 219 | 220 | 221 | ### Features 222 | 223 | * support ts in template expressions ([573fbd2](https://github.com/vuejs/vue-loader/commit/573fbd2e72c3246c2daadb8d8c053464c964cfe3)) 224 | 225 | 226 | # [16.5.0](https://github.com/vuejs/vue-loader/compare/v16.4.1...v16.5.0) (2021-08-07) 227 | 228 | * Custom Elements mode behavior changed: now only inlines the CSS and no longer exports the custom element constructor (exports the component as in normal mode). Users now need to explicitly call `defineCustomElement` on the component. This allows the custom element to be defined using an async version of the source component. 229 | 230 | 231 | ## [16.4.1](https://github.com/vuejs/vue-loader/compare/v16.4.0...v16.4.1) (2021-08-02) 232 | 233 | 234 | ### Bug Fixes 235 | 236 | * fix webpack 5.48 compatibility ([b94289c](https://github.com/vuejs/vue-loader/commit/b94289c9fb395556100ec121529dfe676280d3cd)), closes [#1859](https://github.com/vuejs/vue-loader/issues/1859) 237 | 238 | 239 | # [16.4.0](https://github.com/vuejs/vue-loader/compare/v16.3.3...v16.4.0) (2021-07-30) 240 | 241 | 242 | ### Features 243 | 244 | * customElement option support for Vue 3.2 ([e19fcda](https://github.com/vuejs/vue-loader/commit/e19fcdaa62c4aa5d826c33a0e7fb8786904ee225)) 245 | 246 | 247 | ## [16.3.3](https://github.com/vuejs/vue-loader/compare/v16.3.2...v16.3.3) (2021-07-21) 248 | 249 | 250 | ### Bug Fixes 251 | 252 | * mark @vue/compiler-sfc as an optional peer dependency ([089473a](https://github.com/vuejs/vue-loader/commit/089473af97077b8e14b3feff48d32d2733ad792c)) 253 | 254 | 255 | 256 | ## [16.3.2](https://github.com/vuejs/vue-loader/compare/v16.3.1...v16.3.2) (2021-07-20) 257 | 258 | 259 | ### Bug Fixes 260 | 261 | * add undeclared peer dependency `webpack` and `@vue/compiler-sfc` ([#1853](https://github.com/vuejs/vue-loader/issues/1853)) ([330d672](https://github.com/vuejs/vue-loader/commit/330d672fb344fddefec98e170587d93876a9e354)) 262 | 263 | 264 | 265 | ## [16.3.1](https://github.com/vuejs/vue-loader/compare/v16.3.0...v16.3.1) (2021-07-16) 266 | 267 | 268 | ### Bug Fixes 269 | 270 | * pick up production env in thread-loader context ([821a3a3](https://github.com/vuejs/vue-loader/commit/821a3a35f04cda3154a9341898225f61d72b3f05)), closes [vuejs/vue-next#3921](https://github.com/vuejs/vue-next/issues/3921) 271 | 272 | 273 | 274 | # [16.3.0](https://github.com/vuejs/vue-loader/compare/v16.2.0...v16.3.0) (2021-06-29) 275 | 276 | 277 | ### Features 278 | 279 | * pass on compilerOptions and refSugar when using ` 46 | 47 | 52 | ``` 53 | 54 | There are many cool features provided by `vue-loader`: 55 | 56 | - Allows using other webpack loaders for each part of a Vue component, for example Sass for ` 39 | -------------------------------------------------------------------------------- /example/Button.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | 24 | 29 | 30 | 31 | This component is fire. // <-- this should be logged 32 | 33 | -------------------------------------------------------------------------------- /example/ScriptSetup.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /example/TypeScript.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /example/devServer.js: -------------------------------------------------------------------------------- 1 | // register proxy sub-paths 2 | if (process.env.WEBPACK4) { 3 | console.log('using webpack 4...') 4 | const Module = require('module') 5 | const resolve = Module._resolveFilename 6 | Module._resolveFilename = (request, ...rest) => { 7 | if (request === 'webpack') { 8 | return resolve('webpack4', ...rest) 9 | } 10 | if (request.startsWith('webpack/')) { 11 | return resolve(request.replace(/^webpack\//, 'webpack4/'), ...rest) 12 | } 13 | return resolve(request, ...rest) 14 | } 15 | } 16 | 17 | require('webpack-dev-server/bin/webpack-dev-server.js') 18 | -------------------------------------------------------------------------------- /example/docs-loader.js: -------------------------------------------------------------------------------- 1 | module.exports = function(source) { 2 | this.callback(null, `export default function (Comp) { 3 | Comp.mounted = () => console.log(${JSON.stringify(source.trim())}) 4 | }`) 5 | } 6 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | -------------------------------------------------------------------------------- /example/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs/vue-loader/698636508e08f5379a57eaf086b5ff533af8e051/example/logo.png -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.vue' 2 | import { createApp, createSSRApp } from 'vue' 3 | 4 | const app = __IS_SSR__ ? createSSRApp(App) : createApp(App) 5 | export default app 6 | 7 | if (typeof window !== 'undefined') { 8 | app.mount('#app') 9 | } 10 | -------------------------------------------------------------------------------- /example/ssr.js: -------------------------------------------------------------------------------- 1 | // This script is part of `yarn build-example-ssr`. 2 | 3 | const fs = require('fs') 4 | const path = require('path') 5 | const { renderToString } = require('@vue/server-renderer') 6 | const template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8') 7 | 8 | // here out server-side build directly exports an app instance. 9 | // in an actual SSR setup, you'll want to export a `createApp()` function that 10 | // returns a fresh app instance for each request. You probably also want to 11 | // return the router instance so that you can set the app's route state before 12 | // actually rendering it. 13 | const app = require('./dist-ssr/server/main.js').default 14 | 15 | renderToString(app).then((html) => { 16 | fs.writeFileSync( 17 | path.resolve(__dirname, 'dist-ssr/index.html'), 18 | template.replace(/(
)/, `$1${html}`) 19 | ) 20 | }) 21 | -------------------------------------------------------------------------------- /example/types.ts: -------------------------------------------------------------------------------- 1 | export interface Props { 2 | msg: string 3 | lol?: string 4 | } 5 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const VueLoaderPlugin = require('../dist/plugin').default 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 5 | 6 | module.exports = (env = {}) => { 7 | const isProd = env.prod 8 | const isSSR = env.ssr 9 | 10 | /** 11 | * Some notes regarding config for the server build of an SSR app: 12 | * 1. target: 'node' 13 | * 2. output.libraryTarget: 'commonjs' (so the exported app can be required) 14 | * 3. externals: this is mostly for faster builds. 15 | * - externalize @vue/* deps via commonjs require() 16 | * - externalize client side deps that are never used on the server, e.g. 17 | * ones that are only used in onMounted() to empty modules 18 | * 4. If using cache-loader or any other forms of cache, make sure the cache 19 | * key takes client vs. server builds into account! 20 | */ 21 | const genConfig = (isServerBuild = false) => { 22 | const minimize = isProd && !isServerBuild && !env.noMinimize 23 | 24 | return { 25 | mode: isProd ? 'production' : 'development', 26 | entry: path.resolve(__dirname, './main.js'), 27 | target: isServerBuild ? 'node' : 'web', 28 | devtool: 'source-map', 29 | resolve: { 30 | extensions: ['.js', '.ts'], 31 | alias: process.env.WEBPACK4 32 | ? { 33 | webpack: 'webpack4', 34 | } 35 | : {}, 36 | }, 37 | output: { 38 | path: path.resolve( 39 | __dirname, 40 | isSSR ? (isServerBuild ? 'dist-ssr/server' : 'dist-ssr/dist') : 'dist' 41 | ), 42 | filename: '[name].js', 43 | publicPath: '/dist/', 44 | libraryTarget: isServerBuild ? 'commonjs' : undefined, 45 | }, 46 | externals: isServerBuild 47 | ? [ 48 | (ctx, request, cb) => { 49 | if (/^@vue/.test(request)) { 50 | return cb(null, 'commonjs ' + request) 51 | } 52 | cb() 53 | }, 54 | ] 55 | : undefined, 56 | module: { 57 | rules: [ 58 | { 59 | test: /\.vue$/, 60 | loader: 'vue-loader', 61 | options: { 62 | // reactivityTransform: true, 63 | compilerOptions: { 64 | isCustomElement: (tag) => tag.startsWith('custom-'), 65 | }, 66 | }, 67 | }, 68 | { 69 | test: /\.png$/, 70 | use: [ 71 | { 72 | loader: 'url-loader', 73 | options: { 74 | limit: 8192, 75 | }, 76 | }, 77 | ], 78 | }, 79 | { 80 | test: /\.css$/, 81 | use: [MiniCssExtractPlugin.loader, 'css-loader'], 82 | }, 83 | { 84 | test: /\.ts$/, 85 | use: [ 86 | { 87 | loader: process.env.WEBPACK4 88 | ? require.resolve('ts-loader') 89 | : require.resolve('ts-loader-v9'), 90 | options: { 91 | transpileOnly: true, 92 | appendTsSuffixTo: [/\.vue$/], 93 | }, 94 | }, 95 | ], 96 | }, 97 | // target custom blocks 98 | { 99 | resourceQuery: /blockType=docs/, 100 | loader: require.resolve('./docs-loader'), 101 | }, 102 | ], 103 | }, 104 | plugins: [ 105 | new VueLoaderPlugin(), 106 | new MiniCssExtractPlugin({ 107 | filename: '[name].css', 108 | }), 109 | new webpack.DefinePlugin({ 110 | __IS_SSR__: !!isSSR, 111 | __VUE_OPTIONS_API__: true, 112 | __VUE_PROD_DEVTOOLS__: false, 113 | __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false, 114 | }), 115 | ], 116 | optimization: { 117 | minimize, 118 | }, 119 | devServer: { 120 | hot: true, 121 | stats: 'minimal', 122 | contentBase: __dirname, 123 | overlay: true, 124 | }, 125 | resolveLoader: { 126 | alias: { 127 | 'vue-loader': require.resolve('../'), 128 | }, 129 | }, 130 | } 131 | } 132 | 133 | if (!isSSR) { 134 | return genConfig() 135 | } else { 136 | return [genConfig(), genConfig(true)] 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const isWebpack4 = process.env.WEBPACK4 2 | 3 | console.log( 4 | `running tests with webpack ${isWebpack4 ? '4' : '5'}${ 5 | !isWebpack4 && process.env.INLINE_MATCH_RESOURCE 6 | ? ' with inline match resource enabled' 7 | : '' 8 | }...` 9 | ) 10 | 11 | module.exports = { 12 | preset: 'ts-jest', 13 | testTimeout: 60000, 14 | testEnvironment: 'node', 15 | testPathIgnorePatterns: ['/dist/', '/node_modules/'], 16 | globals: { 17 | 'ts-jest': { 18 | diagnostics: false, 19 | }, 20 | }, 21 | moduleNameMapper: process.env.WEBPACK4 22 | ? { 23 | '^webpack$': 'webpack4', 24 | '^webpack/(.*)': 'webpack4/$1', 25 | } 26 | : undefined, 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-loader", 3 | "version": "17.4.2", 4 | "license": "MIT", 5 | "author": "Evan You", 6 | "repository": "vuejs/vue-loader", 7 | "main": "dist/index.js", 8 | "types": "dist/index.d.ts", 9 | "files": [ 10 | "dist" 11 | ], 12 | "scripts": { 13 | "dev": "tsc --watch", 14 | "build": "tsc", 15 | "pretest": "tsc", 16 | "test": "jest", 17 | "pretest:match-resource": "tsc", 18 | "test:match-resource": "INLINE_MATCH_RESOURCE=true jest", 19 | "pretest:webpack4": "tsc", 20 | "test:webpack4": "WEBPACK4=true jest", 21 | "dev-example": "node example/devServer.js --config example/webpack.config.js --inline --hot", 22 | "build-example": "rm -rf example/dist && webpack --config example/webpack.config.js --env.prod", 23 | "build-example-ssr": "rm -rf example/dist-ssr && webpack --config example/webpack.config.js --env.prod --env.ssr && node example/ssr.js", 24 | "lint": "prettier --write --parser typescript \"{src,test}/**/*.{j,t}s\"", 25 | "prepublishOnly": "tsc && conventional-changelog -p angular -i CHANGELOG.md -s -r 2" 26 | }, 27 | "gitHooks": { 28 | "pre-commit": "lint-staged" 29 | }, 30 | "lint-staged": { 31 | "*.js": [ 32 | "prettier --write" 33 | ], 34 | "*.ts": [ 35 | "prettier --parser=typescript --write" 36 | ] 37 | }, 38 | "packageManager": "pnpm@8.12.0", 39 | "dependencies": { 40 | "chalk": "^4.1.0", 41 | "watchpack": "^2.4.0" 42 | }, 43 | "peerDependencies": { 44 | "webpack": "^4.1.0 || ^5.0.0-0" 45 | }, 46 | "peerDependenciesMeta": { 47 | "@vue/compiler-sfc": { 48 | "optional": true 49 | }, 50 | "vue": { 51 | "optional": true 52 | } 53 | }, 54 | "devDependencies": { 55 | "@babel/core": "^7.7.7", 56 | "@babel/preset-env": "^7.11.5", 57 | "@intlify/vue-i18n-loader": "^3.0.0", 58 | "@types/cssesc": "^3.0.2", 59 | "@types/estree": "^0.0.45", 60 | "@types/jest": "^26.0.13", 61 | "@types/jsdom": "^16.2.13", 62 | "@types/mini-css-extract-plugin": "^0.9.1", 63 | "@types/webpack-merge": "^4.1.5", 64 | "babel-loader": "^8.1.0", 65 | "cache-loader": "^4.1.0", 66 | "conventional-changelog-cli": "^2.1.1", 67 | "css-loader": "^4.3.0", 68 | "cssesc": "^3.0.0", 69 | "file-loader": "^6.1.0", 70 | "html-webpack-plugin": "^4.5.0", 71 | "html-webpack-plugin-v5": "npm:html-webpack-plugin@^5.3.2", 72 | "jest": "^26.4.1", 73 | "jsdom": "^16.4.0", 74 | "lint-staged": "^10.3.0", 75 | "markdown-loader": "^6.0.0", 76 | "memfs": "^3.1.2", 77 | "mini-css-extract-plugin": "^1.6.2", 78 | "normalize-newline": "^3.0.0", 79 | "null-loader": "^4.0.1", 80 | "postcss-loader": "^4.0.4", 81 | "prettier": "^2.1.1", 82 | "pug": "^2.0.0", 83 | "pug-plain-loader": "^1.0.0", 84 | "source-map": "^0.6.1", 85 | "style-loader": "^2.0.0", 86 | "stylus": "^0.54.7", 87 | "stylus-loader": "^4.1.1", 88 | "sugarss": "^3.0.1", 89 | "ts-jest": "^26.2.0", 90 | "ts-loader": "^8.0.6", 91 | "ts-loader-v9": "npm:ts-loader@^9.2.4", 92 | "typescript": "^4.4.3", 93 | "url-loader": "^4.1.0", 94 | "vue": "^3.4.3", 95 | "vue-i18n": "^9.1.7", 96 | "webpack": "^5.79.0", 97 | "webpack-cli": "^3.3.12", 98 | "webpack-dev-server": "^3.11.3", 99 | "webpack-merge": "^5.1.4", 100 | "webpack4": "npm:webpack@^4.46.0", 101 | "yorkie": "^2.0.0" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/compiler.ts: -------------------------------------------------------------------------------- 1 | // extend the descriptor so we can store the scopeId on it 2 | declare module 'vue/compiler-sfc' { 3 | interface SFCDescriptor { 4 | id: string 5 | } 6 | } 7 | 8 | import * as _compiler from 'vue/compiler-sfc' 9 | 10 | export let compiler: typeof _compiler 11 | 12 | try { 13 | // Vue 3.2.13+ ships the SFC compiler directly under the `vue` package 14 | // making it no longer necessary to have @vue/compiler-sfc separately installed. 15 | compiler = require('vue/compiler-sfc') 16 | } catch (e) { 17 | try { 18 | compiler = require('@vue/compiler-sfc') 19 | } catch (e) { 20 | throw new Error( 21 | `@vitejs/plugin-vue requires vue (>=3.2.13) or @vue/compiler-sfc ` + 22 | `to be present in the dependency tree.` 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/cssModules.ts: -------------------------------------------------------------------------------- 1 | export function genCSSModulesCode( 2 | id: string, 3 | index: number, 4 | request: string, 5 | moduleName: string | boolean, 6 | needsHotReload: boolean 7 | ): string { 8 | const styleVar = `style${index}` 9 | let code = `\nimport ${styleVar} from ${request}` 10 | 11 | // inject variable 12 | const name = typeof moduleName === 'string' ? moduleName : '$style' 13 | code += `\ncssModules["${name}"] = ${styleVar}` 14 | 15 | if (needsHotReload) { 16 | code += ` 17 | if (module.hot) { 18 | module.hot.accept(${request}, () => { 19 | cssModules["${name}"] = ${styleVar} 20 | __VUE_HMR_RUNTIME__.rerender("${id}") 21 | }) 22 | }` 23 | } 24 | 25 | return code 26 | } 27 | -------------------------------------------------------------------------------- /src/descriptorCache.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs' 2 | import type { CompilerOptions, SFCDescriptor } from 'vue/compiler-sfc' 3 | import { compiler } from './compiler' 4 | 5 | const { parse } = compiler 6 | export const descriptorCache = new Map() 7 | 8 | export function setDescriptor(filename: string, entry: SFCDescriptor) { 9 | descriptorCache.set(cleanQuery(filename), entry) 10 | } 11 | 12 | export function getDescriptor( 13 | filename: string, 14 | compilerOptions?: CompilerOptions 15 | ): SFCDescriptor { 16 | filename = cleanQuery(filename) 17 | if (descriptorCache.has(filename)) { 18 | return descriptorCache.get(filename)! 19 | } 20 | 21 | // This function should only be called after the descriptor has been 22 | // cached by the main loader. 23 | // If this is somehow called without a cache hit, it's probably due to sub 24 | // loaders being run in separate threads. The only way to deal with this is to 25 | // read from disk directly... 26 | const source = fs.readFileSync(filename, 'utf-8') 27 | const { descriptor } = parse(source, { 28 | filename, 29 | sourceMap: true, 30 | templateParseOptions: compilerOptions, 31 | }) 32 | descriptorCache.set(filename, descriptor) 33 | return descriptor 34 | } 35 | 36 | function cleanQuery(str: string) { 37 | const i = str.indexOf('?') 38 | return i > 0 ? str.slice(0, i) : str 39 | } 40 | -------------------------------------------------------------------------------- /src/exportHelper.ts: -------------------------------------------------------------------------------- 1 | // runtime helper for setting properties on components 2 | // in a tree-shakable way 3 | export default (sfc: any, props: [string, string][]) => { 4 | const target = sfc.__vccOpts || sfc 5 | for (const [key, val] of props) { 6 | target[key] = val 7 | } 8 | return target 9 | } 10 | -------------------------------------------------------------------------------- /src/formatError.ts: -------------------------------------------------------------------------------- 1 | import type { CompilerError } from 'vue/compiler-sfc' 2 | import { compiler } from './compiler' 3 | import chalk = require('chalk') 4 | 5 | const { generateCodeFrame } = compiler 6 | 7 | export function formatError( 8 | err: SyntaxError | CompilerError, 9 | source: string, 10 | file: string 11 | ) { 12 | const loc = (err as CompilerError).loc 13 | if (!loc) { 14 | return 15 | } 16 | const locString = `:${loc.start.line}:${loc.start.column}` 17 | const filePath = chalk.gray(`at ${file}${locString}`) 18 | const codeframe = generateCodeFrame(source, loc.start.offset, loc.end.offset) 19 | err.message = `\n${chalk.red( 20 | `VueCompilerError: ${err.message}` 21 | )}\n${filePath}\n${chalk.yellow(codeframe)}\n` 22 | } 23 | -------------------------------------------------------------------------------- /src/hotReload.ts: -------------------------------------------------------------------------------- 1 | // __VUE_HMR_RUNTIME__ is injected to global scope by @vue/runtime-core 2 | 3 | export function genHotReloadCode( 4 | id: string, 5 | templateRequest: string | undefined 6 | ): string { 7 | return ` 8 | /* hot reload */ 9 | if (module.hot) { 10 | __exports__.__hmrId = "${id}" 11 | const api = __VUE_HMR_RUNTIME__ 12 | module.hot.accept() 13 | if (!api.createRecord('${id}', __exports__)) { 14 | api.reload('${id}', __exports__) 15 | } 16 | ${templateRequest ? genTemplateHotReloadCode(id, templateRequest) : ''} 17 | } 18 | ` 19 | } 20 | 21 | function genTemplateHotReloadCode(id: string, request: string) { 22 | return ` 23 | module.hot.accept(${request}, () => { 24 | api.rerender('${id}', render) 25 | }) 26 | ` 27 | } 28 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { LoaderContext } from 'webpack' 2 | import * as path from 'path' 3 | import * as crypto from 'crypto' 4 | import * as qs from 'querystring' 5 | 6 | import { compiler } from './compiler' 7 | import type { 8 | TemplateCompiler, 9 | CompilerOptions, 10 | SFCBlock, 11 | SFCTemplateCompileOptions, 12 | SFCScriptCompileOptions, 13 | } from 'vue/compiler-sfc' 14 | import { selectBlock } from './select' 15 | import { genHotReloadCode } from './hotReload' 16 | import { genCSSModulesCode } from './cssModules' 17 | import { formatError } from './formatError' 18 | 19 | import VueLoaderPlugin from './plugin' 20 | import { canInlineTemplate } from './resolveScript' 21 | import { setDescriptor } from './descriptorCache' 22 | import { 23 | getOptions, 24 | stringifyRequest as _stringifyRequest, 25 | genMatchResource, 26 | testWebpack5, 27 | } from './util' 28 | 29 | export { VueLoaderPlugin } 30 | 31 | export interface VueLoaderOptions { 32 | // https://babeljs.io/docs/en/next/babel-parser#plugins 33 | babelParserPlugins?: SFCScriptCompileOptions['babelParserPlugins'] 34 | transformAssetUrls?: SFCTemplateCompileOptions['transformAssetUrls'] 35 | compiler?: TemplateCompiler | string 36 | compilerOptions?: CompilerOptions 37 | /** 38 | * TODO remove in 3.4 39 | * @deprecated 40 | */ 41 | reactivityTransform?: boolean 42 | 43 | /** 44 | * @experimental 45 | */ 46 | propsDestructure?: boolean 47 | /** 48 | * @experimental 49 | */ 50 | defineModel?: boolean 51 | 52 | customElement?: boolean | RegExp 53 | 54 | hotReload?: boolean 55 | exposeFilename?: boolean 56 | appendExtension?: boolean 57 | enableTsInTemplate?: boolean 58 | experimentalInlineMatchResource?: boolean 59 | 60 | isServerBuild?: boolean 61 | } 62 | 63 | let errorEmitted = false 64 | 65 | const { parse } = compiler 66 | const exportHelperPath = require.resolve('./exportHelper') 67 | 68 | function hash(text: string): string { 69 | return crypto.createHash('sha256').update(text).digest('hex').substring(0, 8) 70 | } 71 | 72 | export default function loader( 73 | this: LoaderContext, 74 | source: string 75 | ) { 76 | const loaderContext = this 77 | 78 | // check if plugin is installed 79 | if ( 80 | !errorEmitted && 81 | !(loaderContext as any)['thread-loader'] && 82 | !(loaderContext as any)[VueLoaderPlugin.NS] 83 | ) { 84 | loaderContext.emitError( 85 | new Error( 86 | `vue-loader was used without the corresponding plugin. ` + 87 | `Make sure to include VueLoaderPlugin in your webpack config.` 88 | ) 89 | ) 90 | errorEmitted = true 91 | } 92 | 93 | const stringifyRequest = (r: string) => _stringifyRequest(loaderContext, r) 94 | 95 | const { 96 | mode, 97 | target, 98 | sourceMap, 99 | rootContext, 100 | resourcePath, 101 | resourceQuery: _resourceQuery = '', 102 | _compiler, 103 | } = loaderContext 104 | 105 | const isWebpack5 = testWebpack5(_compiler) 106 | const rawQuery = _resourceQuery.slice(1) 107 | const incomingQuery = qs.parse(rawQuery) 108 | const resourceQuery = rawQuery ? `&${rawQuery}` : '' 109 | const options = (getOptions(loaderContext) || {}) as VueLoaderOptions 110 | const enableInlineMatchResource = 111 | isWebpack5 && Boolean(options.experimentalInlineMatchResource) 112 | 113 | const isServer = options.isServerBuild ?? target === 'node' 114 | const isProduction = 115 | mode === 'production' || process.env.NODE_ENV === 'production' 116 | 117 | const filename = resourcePath.replace(/\?.*$/, '') 118 | 119 | const { descriptor, errors } = parse(source, { 120 | filename, 121 | sourceMap, 122 | templateParseOptions: options.compilerOptions, 123 | }) 124 | 125 | const asCustomElement = 126 | typeof options.customElement === 'boolean' 127 | ? options.customElement 128 | : (options.customElement || /\.ce\.vue$/).test(filename) 129 | 130 | // cache descriptor 131 | setDescriptor(filename, descriptor) 132 | 133 | if (errors.length) { 134 | errors.forEach((err) => { 135 | formatError(err, source, resourcePath) 136 | loaderContext.emitError(err) 137 | }) 138 | return `` 139 | } 140 | 141 | // module id for scoped CSS & hot-reload 142 | const rawShortFilePath = path 143 | .relative(rootContext || process.cwd(), filename) 144 | .replace(/^(\.\.[\/\\])+/, '') 145 | const shortFilePath = rawShortFilePath.replace(/\\/g, '/') 146 | const id = hash( 147 | isProduction 148 | ? shortFilePath + '\n' + source.replace(/\r\n/g, '\n') 149 | : shortFilePath 150 | ) 151 | 152 | // if the query has a type field, this is a language block request 153 | // e.g. foo.vue?type=template&id=xxxxx 154 | // and we will return early 155 | if (incomingQuery.type) { 156 | return selectBlock( 157 | descriptor, 158 | id, 159 | options, 160 | loaderContext, 161 | incomingQuery, 162 | !!options.appendExtension 163 | ) 164 | } 165 | 166 | // feature information 167 | const hasScoped = descriptor.styles.some((s) => s.scoped) 168 | const needsHotReload = 169 | !isServer && 170 | !isProduction && 171 | !!(descriptor.script || descriptor.scriptSetup || descriptor.template) && 172 | options.hotReload !== false 173 | 174 | // extra properties to attach to the script object 175 | // we need to do this in a tree-shaking friendly manner 176 | const propsToAttach: [string, string][] = [] 177 | 178 | // script 179 | let scriptImport = `const script = {}` 180 | let isTS = false 181 | const { script, scriptSetup } = descriptor 182 | if (script || scriptSetup) { 183 | const lang = script?.lang || scriptSetup?.lang 184 | isTS = !!(lang && /tsx?/.test(lang)) 185 | const externalQuery = Boolean(script && !scriptSetup && script.src) 186 | ? `&external` 187 | : `` 188 | const src = (script && !scriptSetup && script.src) || resourcePath 189 | const attrsQuery = attrsToQuery((scriptSetup || script)!.attrs, 'js') 190 | const query = `?vue&type=script${attrsQuery}${resourceQuery}${externalQuery}` 191 | 192 | let scriptRequest: string 193 | 194 | if (enableInlineMatchResource) { 195 | scriptRequest = stringifyRequest( 196 | genMatchResource(this, src, query, lang || 'js') 197 | ) 198 | } else { 199 | scriptRequest = stringifyRequest(src + query) 200 | } 201 | 202 | scriptImport = 203 | `import script from ${scriptRequest}\n` + 204 | // support named exports 205 | `export * from ${scriptRequest}` 206 | } 207 | 208 | // template 209 | let templateImport = `` 210 | let templateRequest 211 | const renderFnName = isServer ? `ssrRender` : `render` 212 | const useInlineTemplate = canInlineTemplate(descriptor, isProduction) 213 | if (descriptor.template && !useInlineTemplate) { 214 | const src = descriptor.template.src || resourcePath 215 | const externalQuery = Boolean(descriptor.template.src) ? `&external` : `` 216 | const idQuery = `&id=${id}` 217 | const scopedQuery = hasScoped ? `&scoped=true` : `` 218 | const attrsQuery = attrsToQuery(descriptor.template.attrs) 219 | const tsQuery = 220 | options.enableTsInTemplate !== false && isTS ? `&ts=true` : `` 221 | const query = `?vue&type=template${idQuery}${scopedQuery}${tsQuery}${attrsQuery}${resourceQuery}${externalQuery}` 222 | 223 | if (enableInlineMatchResource) { 224 | templateRequest = stringifyRequest( 225 | genMatchResource( 226 | this, 227 | src, 228 | query, 229 | options.enableTsInTemplate !== false && isTS ? 'ts' : 'js' 230 | ) 231 | ) 232 | } else { 233 | templateRequest = stringifyRequest(src + query) 234 | } 235 | 236 | templateImport = `import { ${renderFnName} } from ${templateRequest}` 237 | propsToAttach.push([renderFnName, renderFnName]) 238 | } 239 | 240 | // styles 241 | let stylesCode = `` 242 | let hasCSSModules = false 243 | const nonWhitespaceRE = /\S+/ 244 | if (descriptor.styles.length) { 245 | descriptor.styles 246 | .filter((style) => style.src || nonWhitespaceRE.test(style.content)) 247 | .forEach((style, i) => { 248 | const src = style.src || resourcePath 249 | const attrsQuery = attrsToQuery(style.attrs, 'css') 250 | const lang = String(style.attrs.lang || 'css') 251 | // make sure to only pass id when necessary so that we don't inject 252 | // duplicate tags when multiple components import the same css file 253 | const idQuery = !style.src || style.scoped ? `&id=${id}` : `` 254 | const inlineQuery = asCustomElement ? `&inline` : `` 255 | const externalQuery = Boolean(style.src) ? `&external` : `` 256 | const query = `?vue&type=style&index=${i}${idQuery}${inlineQuery}${attrsQuery}${resourceQuery}${externalQuery}` 257 | 258 | let styleRequest 259 | if (enableInlineMatchResource) { 260 | styleRequest = stringifyRequest( 261 | genMatchResource(this, src, query, lang) 262 | ) 263 | } else { 264 | styleRequest = stringifyRequest(src + query) 265 | } 266 | 267 | if (style.module) { 268 | if (asCustomElement) { 269 | loaderContext.emitError( 270 | new Error( 271 | ` 20 | -------------------------------------------------------------------------------- /test/fixtures/css-modules-extend.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /test/fixtures/css-modules-simple.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /test/fixtures/css-modules.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /test/fixtures/custom-import.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /test/fixtures/custom-language.vue: -------------------------------------------------------------------------------- 1 | 2 | describe('example', () => { 3 | it('basic', done => { 4 | done(); 5 | }) 6 | }) 7 | 8 | 9 | 10 | This is example documentation for a component. 11 | 12 | 13 | 16 | 17 | 26 | 27 | 32 | -------------------------------------------------------------------------------- /test/fixtures/custom-query.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /test/fixtures/duplicate-cssm.css: -------------------------------------------------------------------------------- 1 | @value color: red; 2 | -------------------------------------------------------------------------------- /test/fixtures/duplicate-cssm.js: -------------------------------------------------------------------------------- 1 | import values from './duplicate-cssm.css' 2 | import Comp from './duplicate-cssm.vue' 3 | 4 | export { values } 5 | export default Comp 6 | 7 | window.exports = values 8 | -------------------------------------------------------------------------------- /test/fixtures/duplicate-cssm.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /test/fixtures/empty-style.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /test/fixtures/entry.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | 3 | import Component from '~target' 4 | import * as exports from '~target' 5 | 6 | if (typeof window !== 'undefined') { 7 | window.componentModule = Component 8 | window.exports = exports 9 | 10 | const app = createApp(Component) 11 | const container = window.document.createElement('div') 12 | container.id = 'app' 13 | window.instance = app.mount(container) 14 | window.document.body.appendChild(container) 15 | } 16 | 17 | export default Component 18 | -------------------------------------------------------------------------------- /test/fixtures/extract-css-chunks.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /test/fixtures/extract-css.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /test/fixtures/i18n-entry.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createI18n } from 'vue-i18n' 3 | 4 | import Component from './i18n.vue' 5 | import * as exports from './i18n.vue' 6 | 7 | const i18n = createI18n({ 8 | locale: 'de', 9 | silentFallbackWarn: true, 10 | silentTranslationWarn: true, 11 | }) 12 | 13 | if (typeof window !== 'undefined') { 14 | window.componentModule = Component 15 | window.exports = exports 16 | 17 | const app = createApp(Component).use(i18n) 18 | 19 | const container = window.document.createElement('div') 20 | window.instance = app.mount(container) 21 | } 22 | 23 | export default Component 24 | -------------------------------------------------------------------------------- /test/fixtures/i18n.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | test: Example Text 7 | 8 | -------------------------------------------------------------------------------- /test/fixtures/imported-types-aliased.ts: -------------------------------------------------------------------------------- 1 | export interface Props { 2 | id?: number 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/imported-types.ts: -------------------------------------------------------------------------------- 1 | export interface Props { 2 | msg: string 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/imported-types.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /test/fixtures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs/vue-loader/698636508e08f5379a57eaf086b5ff533af8e051/test/fixtures/logo.png -------------------------------------------------------------------------------- /test/fixtures/markdown.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /test/fixtures/named-exports.vue: -------------------------------------------------------------------------------- 1 | 9 | Terms 10 | -------------------------------------------------------------------------------- /test/fixtures/no-script.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /test/fixtures/optional-chaining.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | -------------------------------------------------------------------------------- /test/fixtures/postcss.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /test/fixtures/pre.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /test/fixtures/process-custom-file/custom-file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/process-custom-file/process-custom-file.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /test/fixtures/resolve.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /test/fixtures/scoped-css.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 72 | 73 |
74 | 75 | 76 | 83 | -------------------------------------------------------------------------------- /test/fixtures/script-import.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | msg: 'Hello from Component A!', 5 | } 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/script-import.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/fixtures/style-import-scoped.css: -------------------------------------------------------------------------------- 1 | h1 { color: green; } 2 | -------------------------------------------------------------------------------- /test/fixtures/style-import-twice-sub.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /test/fixtures/style-import-twice.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | -------------------------------------------------------------------------------- /test/fixtures/style-import.css: -------------------------------------------------------------------------------- 1 | h1 { color: red; } 2 | -------------------------------------------------------------------------------- /test/fixtures/style-import.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /test/fixtures/style-v-bind.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /test/fixtures/template-import-pre.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/fixtures/template-import.html: -------------------------------------------------------------------------------- 1 |
2 |

hello

3 |
4 | -------------------------------------------------------------------------------- /test/fixtures/template-import.pug: -------------------------------------------------------------------------------- 1 | div 2 | h1 hello 3 | -------------------------------------------------------------------------------- /test/fixtures/template-import.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/fixtures/ts.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /test/fixtures/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "foo": ["./imported-types-aliased.ts"] 5 | } 6 | }, 7 | "include": ["."] 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/unit-test.js: -------------------------------------------------------------------------------- 1 | describe('example', () => { 2 | it('basic', (done) => { 3 | done() 4 | }) 5 | }) 6 | -------------------------------------------------------------------------------- /test/mock-loaders/docs.js: -------------------------------------------------------------------------------- 1 | module.exports = function (source, map) { 2 | this.callback( 3 | null, 4 | `export default Component => { 5 | Component.__docs = ${JSON.stringify(source)} 6 | }`, 7 | map 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /test/mock-loaders/html.js: -------------------------------------------------------------------------------- 1 | module.exports = function (content) { 2 | return content.replace(/red/, 'green') 3 | } 4 | -------------------------------------------------------------------------------- /test/mock-loaders/js.js: -------------------------------------------------------------------------------- 1 | module.exports = function (content) { 2 | return content.replace(/Hello from Component A!/, 'Changed!') 3 | } 4 | -------------------------------------------------------------------------------- /test/mock-loaders/query.js: -------------------------------------------------------------------------------- 1 | module.exports = function (content) { 2 | const query = this.resourceQuery.slice(1) 3 | 4 | if (/change/.test(query)) { 5 | return ` 6 | 9 | 18 | ` 19 | } 20 | 21 | return content 22 | } 23 | -------------------------------------------------------------------------------- /test/script.spec.ts: -------------------------------------------------------------------------------- 1 | import { mockBundleAndRun } from './utils' 2 | 3 | test('named exports', async () => { 4 | const { exports } = await mockBundleAndRun({ 5 | entry: 'named-exports.vue', 6 | }) 7 | expect(exports.default.name).toBe('named-exports') 8 | expect(exports.foo()).toBe(1) 9 | }) 10 | 11 | test('experimental