🌳
pt0/deployF/jsImportsF/getImportedByPathsF.mts
2import * as _ from 'lodash-es'
3import { fileURLToPath } from 'url'
4import path from 'path'
5import fs from 'fs'
6import { execSync } from 'child_process'
16import type { absFileDirPath } from '../../ptDirF.mts'
18// Find markdown files that reference the given pt-relative paths (for updating links when files move)
19const getMdReferencers = (importeePathA: string[]) => {
20 const rgPattern = importeePathA.map(p => p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')
21 try {
22 const result = execSync(
23 `rg -l --glob '*.md' '(${rgPattern})' .`,
24 { encoding: 'utf8', cwd: ptDir, maxBuffer: 10 * 1024 * 1024 }
25 )
26 return result.trim().split('\n').filter(Boolean).map(p => p.replace(/^\.\//, ''))
27 } catch (e: any) {
28 if (e.status === 1) return []
29 throw e
30 }
33export const getPathBinJsScriptPaths = async () => {
34 const pathBinDir = ptDir + '/' + pathBinDirname
35 const allFiles = await lsFilePathsRec(pathBinDir)
36 return _.filter(allFiles, (filePath) => {
37 const firstLine = fs.readFileSync(filePath, 'utf8').split('\n')[0]
38 return _.includes(jsShebangA, firstLine)
39 })
44export const getAllPtCodePaths = async (props?: {importMetaUrl?: string}): Promise<absFileDirPath[]> => {
45 const ctxStore = importMetaUrlCtx.getStore() as {importMetaUrl?: string} | undefined
46 const importMetaUrl = props?.importMetaUrl || ctxStore?.importMetaUrl
47 let allPtFiles = await lsFilePathsRec(ptDir)
49 if (importMetaUrl) {
50 const excludePath = fileURLToPath(importMetaUrl)
51 allPtFiles = allPtFiles.filter(f => f !== excludePath)
52 }
54 const codeExtPaths = _.filter(allPtFiles, isPtCodeFileExt) as absFileDirPath[]
55 const pathBinJsPaths = await getPathBinJsScriptPaths()
56 return [...codeExtPaths, ...pathBinJsPaths]
59const getSubmodulePrefixes = (): string[] => {
60 if (!fs.existsSync(`${ptDir}/.gitmodules`)) return []
61 try {
62 const out = execSync(`git config --file .gitmodules --get-regexp '^submodule\\..*\\.path$'`, {cwd: ptDir, encoding: 'utf8'})
63 return out.trim().split('\n').filter(Boolean).map(line => line.split(' ')[1] + '/')
64 } catch { return [] }
67export const getMdPathsReferencingWord = (word: string): absFileDirPath[] => {
68 try {
69 const result = execSync(`rg -l --glob '*.md' -w ${JSON.stringify(word)} .`, {encoding: 'utf8', cwd: ptDir, maxBuffer: 10 * 1024 * 1024})
70 const relA = result.trim().split('\n').filter(Boolean).map(p => p.replace(/^\.\//, ''))
71 const excludePrefixes = [`${tmpDirName}/`, ...getSubmodulePrefixes()]
72 return relA.filter(p => !excludePrefixes.some(pre => p.startsWith(pre)) && !isProdPatched(p)).map(p => `${ptDir}/${p}` as absFileDirPath)
73 } catch (e: any) { if (e.status === 1) return []; throw e }
76const getImporterCandidatesRg = (importeePathA: string[]) => {
77 const patterns = _.chain(importeePathA).flatMap((importeePath) => {
78 const basename = path.basename(importeePath)
79 const basenameNoExt = basename.replace(/\.[^.]+$/, '')
80 return [basename, basenameNoExt]
81 }).uniq().value()
83 const rgPattern = patterns.join('|')
85 const runRg = (globArg: string) => {
86 try {
87 const result = execSync(
88 `rg -l --hidden '(${rgPattern})' ${globArg} .`,
89 { encoding: 'utf8', cwd: ptDir, maxBuffer: 10 * 1024 * 1024 }
90 )
91 return result.trim().split('\n').filter(Boolean).map(p => p.replace(/^\.\//, ''))
92 } catch (e: any) {
93 if (e.status === 1) return []
94 throw e
95 }
96 }
98 const extMatches = runRg(`--glob '*.{${ptFileExtA.join(',')}}'`)
99 const pathBinMatches = runRg(`--glob '${pathBinDirname}/*'`)
100 return _.uniq([...extMatches, ...pathBinMatches]).filter(p => isPtCodeFileExt(p) || isPathBinJsScript(p))
103export const getPathsThatImport = async ({importeePath, importeePathA: importeePathA_}: {importeePath?: string, importeePathA?: string[]}) => {
104 const importeePathA = importeePathA_ || [importeePath!]
106 const candidatePaths = getImporterCandidatesRg(importeePathA)
107 const depH: Record<string, string[]> = await ptMadge(candidatePaths)
109 const unregMadgeLeafExts = _.chain(depH).map((importedPathA) => importedPathA).flatten().map((p) => {
110 return '.' + _.chain(p).split('.').last().value()
111 }).uniq().reject((ext: string) => !!getIsMadgableLeafExt(ext)).value()
113 const importedByPathsA = _.chain(depH).map((importedPathA, importerPath) => {
114 if (_.intersection(importedPathA, importeePathA).length == 0) return null
115 return importerPath
116 }).compact().value()
118 const shellInvokersA = importeePathA.flatMap(p => getPtnodeShellInvokers(p))
119 const anchorIncludersA = await getAnchorersOfPaths(importeePathA)
120 const mdReferencersA = getMdReferencers(importeePathA)
122 return {importedByPathsA: _.uniq([...importedByPathsA, ...shellInvokersA, ...anchorIncludersA, ...mdReferencersA]), depH, unregMadgeLeafExts}