Skip to content

Commit

Permalink
fix: generate treeshaking friendly code
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Sep 20, 2021
1 parent 573fbd2 commit 11e3cb8
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 17 deletions.
8 changes: 8 additions & 0 deletions src/exportHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// runtime helper for setting properties on components
// in a tree-shakable way
export default (sfc: any, props: [string, string][]) => {
for (const [key, val] of props) {
sfc[key] = val
}
return sfc
}
46 changes: 29 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export interface VueLoaderOptions {

let errorEmitted = false

const exportHelperPath = JSON.stringify(require.resolve('./exportHelper'))

export default function loader(
this: webpack.loader.LoaderContext,
source: string
Expand Down Expand Up @@ -154,6 +156,10 @@ export default function loader(
!!(descriptor.script || descriptor.scriptSetup || descriptor.template) &&
options.hotReload !== false

// extra properties to attach to the script object
// we need to do this in a tree-shaking friendly manner
const propsToAttach: [string, string][] = []

// script
let scriptImport = `const script = {}`
let isTS = false
Expand Down Expand Up @@ -185,6 +191,7 @@ export default function loader(
const query = `?vue&type=template${idQuery}${scopedQuery}${tsQuery}${attrsQuery}${resourceQuery}`
templateRequest = stringifyRequest(src + query)
templateImport = `import { ${renderFnName} } from ${templateRequest}`
propsToAttach.push([renderFnName, renderFnName])
}

// styles
Expand All @@ -210,7 +217,8 @@ export default function loader(
)
}
if (!hasCSSModules) {
stylesCode += `\nconst cssModules = script.__cssModules = {}`
stylesCode += `\nconst cssModules = {}`
propsToAttach.push([`__cssModules`, `cssModules`])
hasCSSModules = true
}
stylesCode += genCSSModulesCode(
Expand All @@ -230,24 +238,20 @@ export default function loader(
// TODO SSR critical CSS collection
})
if (asCustomElement) {
stylesCode += `\nscript.styles = [${descriptor.styles.map(
(_, i) => `_style_${i}`
)}]`
propsToAttach.push([
`styles`,
`[${descriptor.styles.map((_, i) => `_style_${i}`)}]`,
])
}
}

let code = [
templateImport,
scriptImport,
stylesCode,
templateImport ? `script.${renderFnName} = ${renderFnName}` : ``,
]
let code = [templateImport, scriptImport, stylesCode]
.filter(Boolean)
.join('\n')

// attach scope Id for runtime use
if (hasScoped) {
code += `\nscript.__scopeId = "data-v-${id}"`
propsToAttach.push([`__scopeId`, `"data-v-${id}"`])
}

if (needsHotReload) {
Expand All @@ -258,13 +262,14 @@ export default function loader(
if (!isProduction) {
// Expose the file's full path in development, so that it can be opened
// from the devtools.
code += `\nscript.__file = ${JSON.stringify(
rawShortFilePath.replace(/\\/g, '/')
)}`
propsToAttach.push([
`__file`,
JSON.stringify(rawShortFilePath.replace(/\\/g, '/')),
])
} else if (options.exposeFilename) {
// Libraries can opt-in to expose their components' filenames in production builds.
// For security reasons, only expose the file's basename in production.
code += `\nscript.__file = ${JSON.stringify(path.basename(resourcePath))}`
propsToAttach.push([`__file`, JSON.stringify(path.basename(resourcePath))])
}

// custom blocks
Expand All @@ -282,14 +287,21 @@ export default function loader(
const query = `?vue&type=custom&index=${i}${blockTypeQuery}${issuerQuery}${attrsQuery}${resourceQuery}`
return (
`import block${i} from ${stringifyRequest(src + query)}\n` +
`if (typeof block${i} === 'function') block${i}(script)`
`if (typeof block${i} === 'function') /*#__PURE__*/block${i}(script)`
)
})
.join(`\n`) + `\n`
}

// finalize
code += `\n\nexport default script`
if (!propsToAttach.length) {
code += `\n\nexport default script`
} else {
code += `\n\nimport exportComponent from ${exportHelperPath}`
code += `\nexport default /*#__PURE__*/exportComponent(script, [${propsToAttach
.map(([key, val]) => `['${key}',${val}]`)
.join(',')}])`
}
return code
}

Expand Down

3 comments on commit 11e3cb8

@mbrodala
Copy link

@mbrodala mbrodala commented on 11e3cb8 Sep 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused: we got a an update to version 16.6.0 suggested by Renovate including this change but now there is no such release anymore? It's still on NPM though: https://www.npmjs.com/package/vue-loader/v/16.6.0

I noticed that this change breaks the SFC <i18n> block of https://github.com/intlify/vue-i18n-next in a Webpack production build.

@mbrodala
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my report here: intlify/vue-i18n#680

Not sure if this needs to be fixed in vue-18n or vue-loader.

@yyx990803 Any ideas?

@jskitz
Copy link

@jskitz jskitz commented on 11e3cb8 Sep 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change also seems to break compiling SVG files when using vue-svg-loader damianstasik/vue-svg-loader#136 (comment)

Please sign in to comment.