Skip to content

Commit

Permalink
refactor(bundle): improve bundle generation
Browse files Browse the repository at this point in the history
Previously bundles would manually write to the disk which was not great. Now this is all handled natively while retaining source maps which were prone to small consistency issues
  • Loading branch information
cshawaus committed Feb 24, 2024
1 parent 431b55f commit 4cab563
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 39 deletions.
58 changes: 31 additions & 27 deletions src/bundles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import { writeFileSync } from 'fs'
import { join } from 'path'

import { init, parse as parseImports } from 'es-module-lexer'
import MagicString from 'magic-string'

Expand All @@ -9,13 +6,12 @@ import {
getAemClientLibPath,
getEntryPaths,
getReplacementPath,
isOutputChunk,
relativePathPattern,
setEntryPath,
} from './helpers'

import type { ImportSpecifier } from 'es-module-lexer'
import type { InputOptions, NormalizedOutputOptions } from 'rollup'
import type { InputOptions } from 'rollup'
import type { PluginOption } from 'vite'

import type { BundlesImportRewriterOptions } from './types'
Expand Down Expand Up @@ -60,7 +56,7 @@ export function bundlesImportRewriter(options: BundlesImportRewriterOptions): Pl
return null
}

if (!options.publicPath || !options.publicPath.length) {
if (!options?.publicPath.length) {
this.error(
`'publicPath' doesn't appear to be defined, see https://aemvite.dev/guide/faqs/#vite-errors for more information.`,
)
Expand Down Expand Up @@ -89,15 +85,16 @@ export function bundlesImportRewriter(options: BundlesImportRewriterOptions): Pl
let s!: MagicString
const str = () => s || (s = new MagicString(source))

for (let index = 0; index < imports.length; index++) {
const { e: end, d: dynamicIndex, n: importPath, s: start } = imports[index]
for (const element of imports) {
const { e: end, d: dynamicIndex, n: importPath, s: start } = element

if (dynamicIndex === -1 && importPath && relativePathPattern.test(importPath)) {
const replacementPath = getReplacementPath(chunk.fileName, importPath, options, entryAliases)

debug('render chunk (dynamic import) chunk: %s', chunk.fileName)
debug('render chunk (dynamic import) import: %s', importPath)
debug('render chunk (dynamic import) replacement: %s\n', replacementPath)
debug('(render chunk)')
debug('chunk file name: %s', chunk.fileName)
debug('import path: %s', importPath)
debug('updated import path: %s\n', replacementPath)

str().overwrite(start, end, replacementPath)
}
Expand All @@ -113,14 +110,21 @@ export function bundlesImportRewriter(options: BundlesImportRewriterOptions): Pl
return null
},

async writeBundle(rollupOptions, bundles) {
async generateBundle(rollupOptions, output, isWrite) {
const aemClientLibPath = getAemClientLibPath(options)

for (const [fileName, chunk] of Object.entries(bundles)) {
if (!isOutputChunk(chunk) || !chunk.code) {
debug('(generate bundle)')
debug('aem clientlib path: %s', aemClientLibPath)
debug('is write: %s', isWrite)

for (const [fileName, chunk] of Object.entries(output)) {
if (chunk.type !== 'chunk' || !chunk.imports) {
continue
}

debug('(generate bundle)')
debug('bundle name: %s', fileName)

const source = chunk.code

await init
Expand All @@ -140,31 +144,30 @@ export function bundlesImportRewriter(options: BundlesImportRewriterOptions): Pl
let s!: MagicString
const str = () => s || (s = new MagicString(source))

for (let index = 0; index < imports.length; index++) {
const { e: end, d: dynamicIndex, n: importPath, s: start } = imports[index]
for (const element of imports) {
const { e: end, d: dynamicIndex, n: importPath, s: start } = element

// Native imports
if (dynamicIndex === -1 && importPath && relativePathPattern.test(importPath)) {
const replacementPath = getReplacementPath(chunk.fileName, importPath, options, entryAliases)

debug('write bundle (native import) chunk: %s', chunk.fileName)
debug('write bundle (native import) import: %s\n', importPath)
debug('chunk file name: %s', chunk.fileName)
debug('import type: native')
debug('import path: %s\n', importPath)

str().overwrite(start, end, replacementPath)
}

// Dynamic imports
if (dynamicIndex > -1 && importPath) {
debug('write bundle (dynamic import) chunk: %s', chunk.fileName)
debug('write bundle (dynamic import) import: %s\n', importPath)
debug('chunk file name: %s', chunk.fileName)
debug('import type: dynamic')
debug('import path: %s', importPath)

const dynamicEnd = source.indexOf(')', end) + 1
const original = source.slice(dynamicIndex + 8, dynamicEnd - 2)

debug(
'write bundle (dynamic import) replacement:',
getReplacementPath(chunk.fileName, importPath, options, entryAliases),
)
debug('updated import path: %s\n', getReplacementPath(chunk.fileName, importPath, options, entryAliases))

if (!original.startsWith('/')) {
str().overwrite(start + 1, end - 1, getReplacementPath(chunk.fileName, importPath, options, entryAliases))
Expand All @@ -173,10 +176,10 @@ export function bundlesImportRewriter(options: BundlesImportRewriterOptions): Pl
}

let aemImportPath = aemClientLibPath
let newSource = (s && s.toString()) || source
let newSource = s?.toString() ?? source

if (options.caching && options.caching.enabled) {
aemImportPath = getAemClientLibPath(options, false, true, rollupOptions as NormalizedOutputOptions)
aemImportPath = getAemClientLibPath(options, false, true, rollupOptions)
}

// Ensure all entry file imports are replaced with the correct AEM ClientLib path
Expand All @@ -189,7 +192,8 @@ export function bundlesImportRewriter(options: BundlesImportRewriterOptions): Pl
newSource = newSource.replace(new RegExp(path, 'g'), relativeClientLibPath)
})

writeFileSync(join(rollupOptions.dir as string, fileName), newSource)
chunk.code = newSource
chunk.map = rollupOptions.sourcemap !== false ? s.generateMap({ hires: true }) : null
}
},
}
Expand Down
14 changes: 2 additions & 12 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import _debug from 'debug'
import { existsSync, readFileSync } from 'fs'
import { join } from 'path'

import type { InputOptions, NormalizedOutputOptions, OutputAsset, OutputChunk } from 'rollup'
import type { InputOptions, NormalizedOutputOptions } from 'rollup'

import type { AEMLongCacheConfiguration, BundlesImportRewriterOptions } from './types'

Expand All @@ -30,16 +30,6 @@ export function setEntryPath(path: string): void {
entryPaths.add(path)
}

/**
* Determine if the provided `assetOrChunk` object is a chunk instance.
*
* @param assetOrChunk an instance of either `OutputAsset` or `OutputChunk`
* @returns `true` when `OutputChunk`, otherwise `false`
*/
export function isOutputChunk(assetOrChunk: OutputAsset | OutputChunk): assetOrChunk is OutputChunk {
return Array.isArray((assetOrChunk as OutputChunk).imports)
}

/**
* Generate an MD5 checksum for the provided `source` input.
*
Expand Down Expand Up @@ -155,5 +145,5 @@ export function getReplacementPath(
export function isInputAnEntryAlias(input: string, entryAliases: NonNullable<InputOptions['input']>) {
const entryAliasesExpr = new RegExp(`^[./]+(${Object.keys(entryAliases).join('|')})\\.js$`)

return input.match(entryAliasesExpr)?.[0] ? true : false
return !!RegExp(entryAliasesExpr).exec(input)?.[0]
}

0 comments on commit 4cab563

Please sign in to comment.