vite: [vite:build-html] Unable to parse html in build step

Describe the bug

using vite with vite-plugin-string for importing html as strings dev mode works great, but when trying to build for production, we encounter the error below. the expected behavior is to not try to traverse all of my HTMLs as I don’t care if it’s valid or not. my index.html is perfectly valid but my other dozen HTML files that we import as strings are not all valid (in syntax).

[vite:build-html] Unable to parse {"file":"src/app/outer/register/register.html","line":1,"column":29}
1  |  export default "Already have an account?</a>\n                </div>\n            </div>\n\n        </div>\n    </form>\n</div>\n";
   |                              ^
file: src/app/outer/register/register.html
error during build:
Error: Unable to parse {"file":"src/app/outer/register/register.html","line":1,"column":29}
1  |  export default "Already have an account?</a>\n                </div>\n            </div>\n\n        </div>\n    </form>\n</div>\n";
   |                              ^
    at traverseHtml (/node_modules/vite/dist/node/chunks/dep-0ed4fbc0.js:24398:15)
    at async Object.transform (/node_modules/vite/dist/node/chunks/dep-0ed4fbc0.js:24453:17)
    at async ModuleLoader.addModuleSource (/node_modules/vite/node_modules/rollup/dist/shared/rollup.js:18616:30)
    at async ModuleLoader.fetchModule (/node_modules/vite/node_modules/rollup/dist/shared/rollup.js:18672:9)
    at async /vite/node_modules/rollup/dist/shared/rollup.js:18647:48
    at async Promise.all (index 3)
    at async ModuleLoader.fetchDynamicDependencies (/vite/node_modules/rollup/dist/shared/rollup.js:18637:30)
    at async Promise.all (index 1)
    at async ModuleLoader.fetchModule (/node_modules/vite/node_modules/rollup/dist/shared/rollup.js:18674:9)
    at async Promise.all (index 0)

Reproduction

using vite-plugin-string while importing broken html syntax that is not the index.html https://github.com/vitejs/vite/blob/4112c5d103673b83c50d446096086617dfaac5a3/packages/vite/src/node/plugins/html.ts#L153 basically should check only my index.html and not the rest of the HTML tree

System Info

System:
    OS: macOS 11.4
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 5.09 GB / 32.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 15.6.0 - ~/.volta/tools/image/node/15.6.0/bin/node
    Yarn: 1.22.10 - ~/.volta/tools/image/yarn/1.22.10/bin/yarn
    npm: 7.19.0 - ~/.volta/tools/image/npm/7.19.0/bin/npm
  Browsers:
    Chrome: 91.0.4472.114
    Firefox: 84.0
    Safari: 14.1.1

Used Package Manager

yarn

Logs

vite:config using resolved config: {
  vite:config   root: '/app-frontend/src',
  vite:config   logLevel: 'info',
  vite:config   server: {
  vite:config     fsServe: { root: '/Users/***/****', strict: false }
  vite:config   },
  vite:config   resolve: {
  vite:config     dedupe: undefined,
  vite:config     alias: [
  vite:config       [Object], [Object], [Object],
  vite:config       [Object], [Object], [Object],
  vite:config       [Object], [Object], [Object],
  vite:config       [Object], [Object], [Object],
  vite:config       [Object], [Object], [Object],
  vite:config       [Object], [Object], [Object],
  vite:config       [Object], [Object], [Object],
  vite:config       [Object], [Object]
  vite:config     ]
  vite:config   },
  vite:config   plugins: [
  vite:config     'alias',
  vite:config     'react-refresh',
  vite:config     'vite:dynamic-import-polyfill',
  vite:config     'vite:resolve',
  vite:config     'vite:html',
  vite:config     'vite:css',
  vite:config     'vite:esbuild',
  vite:config     'vite:json',
  vite:config     'vite:wasm',
  vite:config     'vite:worker',
  vite:config     'vite:asset',
  vite:config     'vite-plugin-string',
  vite:config     'vite-plugin-checker',
  vite:config     'vite:injectHtml',
  vite:config     'vite:define',
  vite:config     'vite:css-post',
  vite:config     'vite:build-html',
  vite:config     'commonjs',
  vite:config     'vite:data-uri',
  vite:config     'rollup-plugin-dynamic-import-variables',
  vite:config     'vite:import-analysis',
  vite:config     'vite:esbuild-transpile',
  vite:config     'vite:terser',
  vite:config     'vite:reporter'
  vite:config   ],
  vite:config   build: {
  vite:config     target: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
  vite:config     polyfillDynamicImport: false,
  vite:config     outDir: 'dist',
  vite:config     assetsDir: 'assets',
  vite:config     assetsInlineLimit: 4096,
  vite:config     cssCodeSplit: true,
  vite:config     sourcemap: false,
  vite:config     rollupOptions: {},
  vite:config     commonjsOptions: { include: [Array], extensions: [Array] },
  vite:config     minify: 'terser',
  vite:config     terserOptions: {},
  vite:config     cleanCssOptions: {},
  vite:config     write: true,
  vite:config     emptyOutDir: null,
  vite:config     manifest: false,
  vite:config     lib: false,
  vite:config     ssr: false,
  vite:config     ssrManifest: false,
  vite:config     brotliSize: true,
  vite:config     chunkSizeWarningLimit: 500,
  vite:config     watch: null
  vite:config   },
  vite:config   configFile: '/app-frontend/vite.config.js',
  vite:config   configFileDependencies: [],
  vite:config   inlineConfig: {
  vite:config     root: undefined,
  vite:config     base: undefined,
  vite:config     mode: undefined,
  vite:config     configFile: undefined,
  vite:config     logLevel: undefined,
  vite:config     clearScreen: undefined,
  vite:config     build: {}
  vite:config   },
  vite:config   base: '/',
  vite:config   publicDir: '/app-frontend/src/public',
  vite:config   cacheDir: '/app-frontend/node_modules/.vite',
  vite:config   command: 'build',
  vite:config   mode: 'production',
  vite:config   isProduction: false,
  vite:config   env: { BASE_URL: '/', MODE: 'production', DEV: true, PROD: false },
  vite:config   assetsInclude: [Function: assetsInclude],
  vite:config   logger: {
  vite:config     hasWarned: false,
  vite:config     info: [Function: info],
  vite:config     warn: [Function: warn],
  vite:config     warnOnce: [Function: warnOnce],
  vite:config     error: [Function: error],
  vite:config     clearScreen: [Function: clearScreen]
  vite:config   },
  vite:config   createResolver: [Function: createResolver],
  vite:config   optimizeDeps: { esbuildOptions: { keepNames: undefined } }
  vite:config } +25ms

Validations

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 19 (5 by maintainers)

Commits related to this issue

Most upvoted comments

That makes sense, if the .html is imported, then internal plugins shouldn’t be touching it. It would be good to get a fix for this.

But instead of using vite-plugin-string, you can use

import part from './part.html?raw'

And you will get the file content as a string, without internal plugins kicking in.

@patak-dev 's suggestion to use import part from './part.html?raw' worked great – thanks!

@AlonMiz I just used the moduleNameMapper as a solution for the jest problem. I use my custom html plugin, but maybe this works for the raw plugin:

moduleNameMapper: {
  "^(.*).html\\?raw": `\$1.html`
}

i was able to resolve all of the above issues. so for everyone that got here in the future:

  1. ?raw is a valid node/ESM import syntax as it should support urls
  2. eslint issue can be resolved by adding webpack resolver eslint-import-resolver-webpack
  3. ts issue can be resolved by adding declare module '*.html?raw'; to a types.d.ts in your workspace/package folder
  4. the regex i used was (import .* from .*)\.html to $1.html?raw

I would still expect some better solution for ?raw imports. some kind of config

build: {
   raw: {
       extenstions : ['html', 'txt'],
       glob: ['**.html'] // or glob
   }
}

@patak-js it did work eventually, but we still have the caveats i mentioned

  1. we are still using webpack to compile to production. adding ?raw might not work as expected with the webpack syntax. but even if it does, it still feels a bit clumsy.
  2. we get an eslint error Unable to resolve path to module './login.html?raw'.eslintimport/no-unresolved we want to migrate all of the features we need in vite+rollup
  3. the third downside is that we need to go over each file of our hundreds of HTMLs imports or create some regex to do that. possible but not convenient.
  4. apparently TS have issues with that as well

@bluwy sorry, don’t remember. Just close it

@dimfeld @rpivo Thanks a lot for that!

I can report that the issue has been resolved we are moving towards replacing our webpack and enjoying vite super fast watch and compilation

yarn run v1.22.10
$ vite build --emptyOutDir
vite v2.7.10 building for production...
transforming (4760) 
✓ 4768 modules transformed.
../dist/assets/apple-icon-57x57.d2fdfdf7.png                    4.40 KiB
../dist/assets/apple-icon-60x60.fd6ea6b3.png                    4.67 KiB
...
✨  Done in 33.77s.

while webpack takes 64.92s

@patak-js thanks ok, that could work EDIT: unfortunately, that does not work. tried replacing all imports with regex to XXX.index?raw, but getting the same error EDIT2: I had some other issue, and now it does work but with all the below caveats

but it has several downsides with that:

  1. we are still using webpack to compile to production. adding ?raw might not work as expected with the webpack syntax. but even if it does, it still feels a bit clumsy.
  2. we get an eslint error Unable to resolve path to module './login.html?raw'.eslintimport/no-unresolved we want to migrate all of the features we need in vite+rollup
  3. the third downside is that we need to go over each file of our hundreds of HTMLs imports, or create some regex to do that. possible but not convinient.
  4. apparently TS have issues with that as well
src/admin/admin.app.ts:27:36 - error TS2307: Cannot find module '../directives/app-breadcrumbs/app-breadcrumbs.html?raw' or its corresponding type declarations.

27 import appBreadCrumbsTemplate from '../directives/app-breadcrumbs/app-breadcrumbs.html?raw';

alternative:

  1. there should be a config that defines what extensions/glob/includes one wants to load as raw. looking at some places encountering that same issue vite-cannot-handle-xxx-html-files vite-in-a-simple-html-js-usecase
  2. as you’ve said, if I’m using a plugin that transpiles a file, vite should not assume it needs to do it either.