/* @flow */ import * as t from 'babel-types' import type { File, Context, FileChunk } from 'pundle-api/types' import { SourceMapGenerator, SourceMapConsumer } from 'source-map' export function getName(obj: Object): string { if (typeof obj.name === 'string') { return obj.name } const chunks = [] if (typeof obj.object === 'object') { chunks.push(getName(obj.object)) } if (typeof obj.property === 'object') { chunks.push(getName(obj.property)) } return chunks.join('.') } const STRING_REGEX = /^"[^"]*"$/ // TODO: use babel-template export function getParsedReplacement(rawValue: any): Object { let parsedValue if (STRING_REGEX.test(rawValue)) { // Extract value between "" // Unescape backward slahes parsedValue = t.stringLiteral(JSON.parse(rawValue)) } else if (typeof rawValue === 'number') { parsedValue = t.numericLiteral(rawValue) } else { parsedValue = t.identifier(rawValue) } return parsedValue } // TODO: Validate the path and disallow bad characters in chunk name // because we use that name as label when writing to disk export function processEnsure(context: Context, file: File, chunks: Array, path: Object) { const [nodeEntry, nodeCallback, nodeName] = path.node.arguments const chunk = context.getChunk(null, nodeName && nodeName.type === 'StringLiteral' ? nodeName.value : null) nodeEntry.elements.forEach((element) => { chunk.addImport(context.getImportRequest(element.value, file.filePath)) }) if (nodeCallback && nodeCallback.params.length) { const nodeCallbackParam = nodeCallback.params[0] path.scope.traverse(nodeCallback, { CallExpression({ node, scope }) { if (getName(node.callee) === nodeCallbackParam.name && !scope.getBinding(nodeCallbackParam.name)) { const argument = node.arguments[0] const request = context.getImportRequest(argument.value, file.filePath) chunk.addImport(request) node.arguments[0].value = request.id.toString() } }, }) } // NOTE: Replace node entry with the new chunk id because we no longer need entry anywhere path.node.arguments[0] = t.stringLiteral(chunk.getId().toString()) path.node.arguments[2] = nodeCallback chunks.push(chunk) } export function processImport(context: Context, file: File, chunks: Array, path: Object) { const chunk = context.getChunk() const argument = path.node.arguments[0] if (!argument || argument.type !== 'StringLiteral') { return } const importRequest = context.getImportRequest(argument.value, file.filePath) chunk.addImport(importRequest) path.replaceWith(t.callExpression( t.identifier('require.import'), [t.stringLiteral(chunk.getId().toString()), t.stringLiteral(importRequest.id.toString())], )) chunks.push(chunk) } export function incrementSourceMapLines(sourceMap: Object, filePath: string, contents: string, linesToIncrement: number): Object { const newMap = new SourceMapGenerator() const entryMap = new SourceMapConsumer(sourceMap) for (let i = 0, length = entryMap._generatedMappings.length; i < length; i++) { const mapping = entryMap._generatedMappings[i] newMap.addMapping({ source: filePath, original: { line: mapping.originalLine, column: mapping.originalColumn }, generated: { line: mapping.generatedLine + linesToIncrement, column: mapping.generatedColumn }, }) } newMap.setSourceContent(filePath, contents) return newMap.toJSON() }