UNPKG

@ali-i18n-fe/dada-component

Version:
191 lines (157 loc) 5.04 kB
const { getProgram } = require("@ali-i18n-fe/react-docgen-typescript-loader-add-tag/dist/loader"); const ts = require("typescript"); const path = require("path"); const fs = require("fs"); const { buildPublicPath, getPreviewPathByFilePath } = require("../utils"); const currentPath = process.cwd(); module.exports = function loader(source) { // Loaders can operate in either synchronous or asynchronous mode. Errors in // asynchronous mode should be reported using the supplied callback. // Will return a callback if operating in asynchronous mode. const callback = this.async(); try { const newSource = processResource.call(this, source); if (!callback) return newSource; callback(null, newSource); return; } catch (e) { if (callback) { callback(e); return; } throw e; } }; function processResource(source) { const context = this; const isProduction = context.mode === "production"; const filePath = context.resourcePath; let previewFile, previewPic; if (filePath) { // only for docs exist previewFile = path.resolve(filePath, "..", "docs.tsx"); if (!fs.existsSync(previewFile)) { return source; } const srcPic = path.resolve(filePath, "..", "preview.png"); const distPic = srcPic.replace( path.resolve(currentPath + "/src/"), path.resolve(currentPath + "/dist/docs/") ); if (fs.existsSync(srcPic)) { previewPic = srcPic; } else if (isProduction && fs.existsSync(distPic)) { previewPic = distPic; } } context.cacheable(true); const program = getProgram(path.resolve(currentPath, "src")); const sourceFile = program.getSourceFile(filePath); if (!sourceFile) { return source; } sourceFile.hasBeenIncrementallyParsed = false; const newSourceFile = ts.updateSourceFile(sourceFile, source, { span: { start: 0, length: sourceFile.text.length }, newLength: source.length }); const checker = program.getTypeChecker(); ts.bindSourceFile(newSourceFile, {}); const moduleSymbol = checker.getSymbolAtLocation(newSourceFile); const exportsModule = checker.getExportsOfModule(moduleSymbol); if (!exportsModule) { return source; } const funMaps = exportsModule .map(exportModel => { const { declarations } = exportModel; const statement = declarations.find( statement => statement.kind === ts.SyntaxKind.ClassDeclaration || statement.kind === ts.SyntaxKind.ExportAssignment || statement.kind === ts.SyntaxKind.FunctionDeclaration ); if (statement) { const name = statement.name || statement.expression || {}; if (!name.text) { return null; } const { pos, end } = statement || {}; return { name: name.text, start: pos, end }; } }) .filter(Boolean); const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); const printNode = sourceNode => printer.printNode(ts.EmitHint.Unspecified, sourceNode, sourceFile); const relativeFilename = buildPublicPath(context, previewFile); const properties = [ { key: "__docsPath", value: relativeFilename }, { key: "displayName", value: "$1" }, { key: "__displayName", value: "$1" } ]; previewPic && properties.push({ key: "__previewPath", value: getPreviewPathByFilePath(context, previewPic) }); const blocks = funMaps.map(funMap => { const { name, end } = funMap; const codeBlocks = ts.createTry( ts.createBlock(createStatement(name, properties), true), ts.createCatchClause( ts.createVariableDeclaration(ts.createIdentifier("__docsPath_error")), ts.createBlock([]) ), undefined ); return [printNode(codeBlocks), end]; }); return addBlocks(source, blocks); } function addBlocks(source, blocks) { let addCount = 0; let result = source; blocks.forEach(block => { const [content, pos] = block; const insertPos = pos + addCount; result = result.slice(0, insertPos) + content + result.slice(insertPos); addCount += content.length; }); return result; } function createStatement(funName, properties) { return properties.map(({ key, value }) => insertTsIgnoreBeforeStatement( ts.createStatement( ts.createBinary( ts.createPropertyAccess( ts.createIdentifier(funName), ts.createIdentifier(key) ), ts.SyntaxKind.EqualsToken, ts.createLiteral(funName.replace(/(.+)/, value)) ) ) ) ); } function createStatements(funNames, properties) { return funNames.reduce( (prev, funName) => prev.concat(createStatement(funName, properties)), [] ); } function insertTsIgnoreBeforeStatement(statement) { ts.setSyntheticLeadingComments(statement, [ { text: " @ts-ignore", // Leading space is important here kind: ts.SyntaxKind.SingleLineCommentTrivia, pos: -1, end: -1 } ]); return statement; }