{
    "cells": [
        {
            "language": "typescript",
            "source": [
                "//@ts-ignore\nimport fs from \"fs\";\n//@ts-ignore\nimport path from \"path\";\n\nconst source = \"/Users/lai/Documents/files.noindex/GitHub/lingo3d/lingo3d/lib\";\nconst out =\n  \"/Users/lai/Documents/files.noindex/GitHub/lingo3d/lingo3d/public/assets/editor/lingo3d.d.ts\";\nconst namespace = \"lingo3d\";\n\nconst fileTree: Record<string, any> = {};\n\nconst parentNodeMap = new WeakMap<Record<string, any>, Record<string, any>>();\nconst nodePathMap = new WeakMap<Record<string, any>, string>();\nconst makeNode = (parent: Record<string, any>, path: string) => {\n  const result: Record<string, any> = {};\n  parentNodeMap.set(result, parent);\n  nodePathMap.set(result, path);\n  return result;\n};\n\ntype FileNode = {\n  isFileNode: true;\n  modulePath: string;\n  name: string;\n  text: string;\n};\n\nconst isFileNode = (target: any): target is FileNode => target.isFileNode;\n\nconst iStart = source.split(\"/\").length - 1;\nconst aggregate = (pathArray: Array<string>, iEnd: number) => {\n  let result = \"\";\n  for (let i = iStart; i <= iEnd; ++i) result += pathArray[i] + \"/\";\n  result = result.slice(0, -1);\n  return result;\n};\n\nconst set = (\n  obj: Record<string, any>,\n  pathArray: Array<string>,\n  value: FileNode\n) => {\n  let target = obj;\n  const iMax = pathArray.length - 1;\n  for (let i = 0; i < iMax; ++i)\n    target = target[pathArray[i]] ??= makeNode(target, aggregate(pathArray, i));\n  target[pathArray[iMax]] = value;\n  parentNodeMap.set(value, target);\n  nodePathMap.set(value, aggregate(pathArray, iMax));\n};\n\nconst loopThroughFiles = (directoryPath: string) => {\n  for (const file of fs.readdirSync(directoryPath)) {\n    const filePath = path.join(directoryPath, file);\n    const stats = fs.statSync(filePath);\n    if (stats.isDirectory()) {\n      loopThroughFiles(filePath);\n      continue;\n    }\n    const parts = filePath.split(\"/\");\n    parts.shift();\n    set(fileTree, parts, {\n      isFileNode: true,\n      modulePath: filePath.slice(source.length + 1),\n      name: file,\n      text: fs.readFileSync(filePath, \"utf8\"),\n    });\n  }\n};\nloopThroughFiles(source);\n\nconst dtsFiles: Array<FileNode> = [];\nconst traverse = (node: Record<string, any>) => {\n  for (const file of Object.values(node))\n    if (isFileNode(file)) file.name.endsWith(\".d.ts\") && dtsFiles.push(file);\n    else traverse(file);\n};\ntraverse(fileTree);\n\nconst normalizeDTS = (value: string, parent: Record<string, any>) => {\n  if (value.endsWith(\".d.ts\")) return value;\n  const test = value + \".d.ts\";\n  if (test in parent) return test;\n  return value;\n};\n\nconst computeImportPath = (parts: Array<string>, dtsFile: FileNode) => {\n  parts = parts.filter((p) => p && p !== \"/\");\n\n  let current: Record<string, any> = parentNodeMap.get(dtsFile)!;\n  for (let i = 0; i < parts.length; ++i) {\n    const part =\n      i === parts.length - 1 ? normalizeDTS(parts[i], current) : parts[i];\n    if (part === \"../\" || part === \"..\") {\n      current = parentNodeMap.get(current)!;\n    } else if (part === \"./\" || part === \".\") {\n      current = current;\n    } else {\n      current = current[part];\n    }\n  }\n  const resolved = nodePathMap.get(current)!;\n  let result = `${namespace}/${\n    resolved.endsWith(\".d.ts\")\n      ? resolved.slice(0, -5)\n      : resolved.endsWith(\"/\")\n      ? resolved.slice(0, -1)\n      : resolved\n  }`;\n  if (result.endsWith(\"/index\")) result = result.slice(0, -6);\n  else if (result.endsWith(\"/index.js\") || result.endsWith(\"/index.ts\"))\n    result = result.slice(0, -9);\n  return result.endsWith(\"/\") ? result.slice(0, -1) : result;\n};\n\nconst modules = new Map<string, string>();\nconst imports = new Set<string>();\nconst externalImports = new Set<string>();\n\nfor (const dtsFile of dtsFiles) {\n  //@ts-ignore\n  dtsFile.text = dtsFile.text.replaceAll(\"declare \", \"\");\n\n  const delimiters = /(\\.\\.\\/|\\.\\/|\\/)/;\n\n  const fromMatches = dtsFile.text.match(/ from \"\\..*?\"/g);\n  for (const match of fromMatches ?? []) {\n    const modulePath = match.slice(7, -1);\n    const parts = modulePath.split(delimiters);\n    if (parts[0] && modulePath !== \"..\" && modulePath !== \".\") {\n      externalImports.add(modulePath);\n      continue;\n    }\n    const importPath = computeImportPath(parts, dtsFile);\n    dtsFile.text = dtsFile.text.replace(match, ` from \"${importPath}\"`);\n    imports.add(importPath);\n  }\n\n  const importMatches = dtsFile.text.match(/\\bimport \"\\..*?\"/g);\n  for (const match of importMatches ?? []) {\n    const modulePath = match.slice(8, -1);\n    const parts = modulePath.split(delimiters);\n    if (parts[0] && modulePath !== \"..\" && modulePath !== \".\") {\n      externalImports.add(modulePath);\n      continue;\n    }\n    const importPath = computeImportPath(parts, dtsFile);\n    dtsFile.text = dtsFile.text.replace(match, `import \"${importPath}\"`);\n    imports.add(importPath);\n  }\n\n  const dynamicImportMatches = dtsFile.text.match(/\\bimport\\(\".*?\"\\)/g);\n  for (const match of dynamicImportMatches ?? []) {\n    const modulePath = match.slice(8, -2);\n    const parts = modulePath.split(delimiters);\n    if (parts[0] && modulePath !== \"..\" && modulePath !== \".\") {\n      externalImports.add(modulePath);\n      continue;\n    }\n    const importPath = computeImportPath(parts, dtsFile);\n    dtsFile.text = dtsFile.text.replace(match, `import(\"${importPath}\")`);\n    imports.add(importPath);\n  }\n\n  let modulePath = `${namespace}/${dtsFile.modulePath.slice(0, -5)}`;\n  if (dtsFile.modulePath.endsWith(\"index.d.ts\"))\n    modulePath = modulePath.slice(0, -6);\n  modules.set(\n    modulePath,\n    `declare module \"${modulePath}\" {\\n${dtsFile.text}}\\n`\n  );\n}\n\nconst prunedModules: Array<string> = [];\nfor (const importPath of imports) {\n  if (!modules.has(importPath)) {\n    console.log(importPath);\n    throw new Error();\n  }\n  prunedModules.push(modules.get(importPath)!);\n}\n\nlet bundle = \"\";\nfor (const module of prunedModules) bundle += module;\n\nfs.writeFileSync(out, bundle, \"utf8\");\nconsole.log(\"done\");\nconsole.log(externalImports);\n"
            ],
            "outputs": [
                {
                    "items": [
                        {
                            "mime": "application/vnd.code.notebook.stdout",
                            "value": [
                                "done",
                                "Set(8) {",
                                "  'three',",
                                "  'xstate',",
                                "  'preact',",
                                "  'preact/compat',",
                                "  'preact/hooks',",
                                "  '@lincode/promiselikes',",
                                "  'monaco-editor',",
                                "  '@lincode/reactivity'",
                                "}",
                                ""
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}