'use strict'
const fs = require('fs')
const path = require('path')
const FileRequireTransform = require('./file-require-transform')
const indentStringImport = import('indent-string')
const {SourceMapGenerator} = require('source-map')
module.exports = async function (cache, options) {
const {default: indentString} = await indentStringImport
const moduleASTs = {}
const requiredModulePaths = [options.mainPath]
const includedFilePaths = new Set(requiredModulePaths)
while (requiredModulePaths.length > 0) {
const filePath = requiredModulePaths.shift()
let relativeFilePath = path.relative(options.baseDirPath, filePath).replace(/\\/g, '/')
if (!relativeFilePath.startsWith('.')) {
relativeFilePath = './' + relativeFilePath
}
if (!moduleASTs[relativeFilePath]) {
const source = fs.readFileSync(filePath, 'utf8')
let foundRequires = []
const transform = new FileRequireTransform({
filePath,
source,
baseDirPath: options.baseDirPath,
didFindRequire: (unresolvedPath, resolvedPath) => {
if (options.shouldExcludeModule({requiringModulePath: filePath, requiredModulePath: resolvedPath})) {
return true
} else {
foundRequires.push({unresolvedPath, resolvedPath})
return false
}
}
})
const cachedTransform = await cache.get({filePath, content: source})
const useCachedTransform =
cachedTransform ?
cachedTransform.requires.every(r => (transform.resolveModulePath(r.unresolvedPath) || r.unresolvedPath) === r.resolvedPath) :
false
let transformedSource, requires
if (useCachedTransform) {
transformedSource = cachedTransform.source
foundRequires = cachedTransform.requires
} else {
try {
transformedSource = indentString(transform.apply(), 2)
} catch (e) {
console.error(`Unable to transform source code for module ${filePath}.`)
if (e.index) {
const before = source.slice(e.index - 100, e.index)
const after = source.slice(e.index, e.index + 100)
console.error(`\n${before}==>${after}\n`)
}
throw e
}
await cache.put({filePath, original: source, transformed: transformedSource, requires: foundRequires})
}
moduleASTs[relativeFilePath] = `function (exports, module, __filename, __dirname, require, define) {\n${transformedSource}\n}`
const resolvedRequirePaths = foundRequires.map(r => r.resolvedPath)
for (let i = 0; i < foundRequires.length; i++) {
const {resolvedPath} = foundRequires[i]
requiredModulePaths.push(resolvedPath)
includedFilePaths.add(resolvedPath)
}
}
}
await cache.deleteUnusedEntries()
let snapshotScript = fs.readFileSync(path.join(__dirname, 'blueprint.js'), 'utf8')
let relativeFilePath = path.relative(options.baseDirPath, options.mainPath).replace(/\\/g, '/')
if (!relativeFilePath.startsWith('.')) {
relativeFilePath = './' + relativeFilePath
}
snapshotScript = snapshotScript.replace('mainModuleRequirePath', JSON.stringify(relativeFilePath))
snapshotScript = snapshotScript.replace('processPlatform', process.platform)
snapshotScript = snapshotScript.replace('const pathSeparator = null', `const pathSeparator = ${JSON.stringify(path.sep)}`)
const auxiliaryData = JSON.stringify(options.auxiliaryData || {})
const auxiliaryDataAssignment = 'var snapshotAuxiliaryData = {}'
const auxiliaryDataAssignmentStartIndex = snapshotScript.indexOf(auxiliaryDataAssignment)
const auxiliaryDataAssignmentEndIndex = auxiliaryDataAssignmentStartIndex + auxiliaryDataAssignment.length
snapshotScript =
snapshotScript.slice(0, auxiliaryDataAssignmentStartIndex) +
`var snapshotAuxiliaryData = ${auxiliaryData};` +
snapshotScript.slice(auxiliaryDataAssignmentEndIndex)
const definitionsAssignment = 'customRequire.definitions = {}'
const definitionsAssignmentStartIndex = snapshotScript.indexOf(definitionsAssignment)
const definitionsAssignmentEndIndex = definitionsAssignmentStartIndex + definitionsAssignment.length
const sections = []
let sectionStartRow = getLineCount(snapshotScript.slice(0, definitionsAssignmentStartIndex)) + 1
let definitions = ''
const moduleFilePaths = Object.keys(moduleASTs)
for (let i = 0; i < moduleFilePaths.length; i++) {
const relativePath = moduleFilePaths[i]
const source = moduleASTs[relativePath]
const lineCount = getLineCount(source)
sections.push({relativePath, startRow: sectionStartRow, endRow: (sectionStartRow + lineCount) - 2})
definitions += indentString(`${JSON.stringify(relativePath)}: ${source}`, 4) + ',\n'
sectionStartRow += lineCount
}
snapshotScript =
snapshotScript.slice(0, definitionsAssignmentStartIndex) +
`customRequire.definitions = {\n${definitions}\n };` +
snapshotScript.slice(definitionsAssignmentEndIndex)
const sectionsAssignment = 'snapshotAuxiliaryData.snapshotSections = []'
const sectionsAssignmentStartIndex = snapshotScript.indexOf(sectionsAssignment)
const sectionsAssignmentEndIndex = sectionsAssignmentStartIndex + sectionsAssignment.length
snapshotScript =
snapshotScript.slice(0, sectionsAssignmentStartIndex) +
`snapshotAuxiliaryData.snapshotSections = ${JSON.stringify(sections)}` +
snapshotScript.slice(sectionsAssignmentEndIndex)
return {snapshotScript, includedFilePaths}
}
function getLineCount (text) {
let lineCount = 1
for (let i = 0; i < text.length; i++) {
if (text[i] === '\n') lineCount++
}
return lineCount
}