11import path from 'path' ;
2+ import fs from 'fs' ;
3+ import { tmpdir } from 'os' ;
24import * as ts from 'typescript' ;
35import { Graph , Meta , Node , OptionValues , Relation } from '../models' ;
46import { pipe , piped } from 'remeda' ;
@@ -24,13 +26,6 @@ export function createGraph(
2426 const rootDir = splitedConfigPath
2527 . slice ( 0 , splitedConfigPath . length - 1 )
2628 . join ( '/' ) ;
27- const { options, fileNames } = ts . parseJsonConfigFileContent (
28- config ,
29- ts . sys ,
30- rootDir ,
31- ) ;
32- options . rootDir = rootDir ;
33- const program = ts . createProgram ( fileNames , options ) ;
3429
3530 const bindWords_isFileNameMatchSomeWords =
3631 ( array : string [ ] ) => ( filename : string ) =>
@@ -41,13 +36,99 @@ export function createGraph(
4136 const isNotMatchSomeExclude = ( filename : string ) =>
4237 ! isMatchSomeExclude ( filename ) ;
4338
39+ if ( false ) {
40+ const { options, fileNames : fullFilePaths } = ts . parseJsonConfigFileContent (
41+ config ,
42+ ts . sys ,
43+ rootDir ,
44+ ) ;
45+ options . rootDir = rootDir ;
46+ const program = ts . createProgram ( fullFilePaths , options ) ;
47+ const graphs = program
48+ . getSourceFiles ( )
49+ . filter ( sourceFile => ! sourceFile . fileName . includes ( 'node_modules' ) ) // node_modules 配下のファイルは除外
50+ . filter ( piped ( getFilePath ( options ) , removeSlash , isNotMatchSomeExclude ) )
51+ . map ( analyzeSoucreFile ( options ) ) ;
52+ return { graph : mergeGraph ( ...graphs ) , meta : { rootDir } } ;
53+ } else {
54+ const graph = createGraphForVue ( rootDir , config , isNotMatchSomeExclude ) ;
55+ return { graph, meta : { rootDir } } ;
56+ }
57+ }
58+
59+ function createGraphForVue (
60+ rootDir : string ,
61+ config : any ,
62+ isNotMatchSomeExclude : ( filename : string ) => boolean ,
63+ ) {
64+ const relativeRootDir = pipe ( path . relative ( process . cwd ( ) , rootDir ) , str =>
65+ str === '' ? './' : str ,
66+ ) ;
67+
68+ // vue と TS ファイルのパスを保持する。その際、すでに *.vue.ts ファイルが存在している場合は対象外とする。
69+ const vueAndTsFilePaths = getVueAndTsFilePathsRecursive (
70+ relativeRootDir ,
71+ ) . filter ( path => ! fs . existsSync ( `${ path } .ts` ) ) ;
72+
73+ const tmpDir = fs . mkdtempSync ( path . join ( tmpdir ( ) , 'tsg-vue-' ) ) ;
74+ console . log ( 'tmpDir:' , tmpDir ) ;
75+ vueAndTsFilePaths
76+ . map ( fullPath => path . relative ( process . cwd ( ) , fullPath ) )
77+ . forEach ( relativeFilePath => {
78+ const tmpFilePath = path . join (
79+ tmpDir ,
80+ // *.vue のファイルは *.vue.ts としてコピー
81+ relativeFilePath . endsWith ( '.vue' )
82+ ? relativeFilePath + '.ts'
83+ : relativeFilePath ,
84+ ) ;
85+ if ( ! tmpFilePath . startsWith ( tmpDir ) ) {
86+ // tmpDir 以外へのコピーを抑止する
87+ return ;
88+ }
89+ fs . mkdirSync ( path . dirname ( tmpFilePath ) , { recursive : true } ) ;
90+ fs . copyFileSync ( relativeFilePath , tmpFilePath ) ;
91+ } ) ;
92+
93+ const { options, fileNames : fullFilePaths } = ts . parseJsonConfigFileContent (
94+ config ,
95+ ts . sys ,
96+ path . join ( tmpDir , relativeRootDir ) ,
97+ ) ;
98+ // rootDir を設定しない場合、 tmpDir/rootDir である場合に `rootDir/` が node についてしまう
99+ options . rootDir = path . join ( tmpDir , relativeRootDir ) ;
100+ // ↑ここまでで、ファイルを tmp にコピーし、新たな fileNames と options を生成する
101+ const program = ts . createProgram ( fullFilePaths , options ) ;
102+ function renameNode ( node : Node ) {
103+ return {
104+ ...node ,
105+ path : node . path
106+ . replace ( '.vue.ts' , '.vue' )
107+ . replace ( `${ tmpDir . slice ( 1 ) } /` , '' ) ,
108+ name : node . name
109+ . replace ( '.vue.ts' , '.vue' )
110+ . replace ( `${ tmpDir . slice ( 1 ) } /` , '' ) ,
111+ } ;
112+ }
44113 const graphs = program
45114 . getSourceFiles ( )
46115 . filter ( sourceFile => ! sourceFile . fileName . includes ( 'node_modules' ) ) // node_modules 配下のファイルは除外
47116 . filter ( piped ( getFilePath ( options ) , removeSlash , isNotMatchSomeExclude ) )
48- . map ( analyzeSoucreFile ( options ) ) ;
49-
50- return { graph : mergeGraph ( ...graphs ) , meta : { rootDir } } ;
117+ . map ( analyzeSoucreFile ( options ) )
118+ . map ( graph => {
119+ // graph においては .vue.ts ファイルを .vue に戻す
120+ return {
121+ nodes : graph . nodes . map ( renameNode ) ,
122+ relations : graph . relations . map ( relation => {
123+ return {
124+ ...relation ,
125+ from : renameNode ( relation . from ) ,
126+ to : renameNode ( relation . to ) ,
127+ } ;
128+ } ) ,
129+ } ;
130+ } ) ;
131+ return mergeGraph ( ...graphs ) ;
51132}
52133
53134function getName ( filePath : string ) {
@@ -104,10 +185,6 @@ function analyzeSoucreFile(
104185 const importPaths : ( string | undefined ) [ ] = [ ] ;
105186 function getModuleNameText ( node : ts . Node ) {
106187 if ( ts . isImportDeclaration ( node ) ) {
107- console . log (
108- 'isImportDeclaration' ,
109- node . moduleSpecifier ?. getText ( sourceFile ) ,
110- ) ;
111188 importPaths . push ( node . moduleSpecifier ?. getText ( sourceFile ) ) ;
112189 } else if ( ts . isCallExpression ( node ) ) {
113190 const text = node . getText ( sourceFile ) ;
@@ -120,19 +197,12 @@ function analyzeSoucreFile(
120197 ts . forEachChild ( node , getModuleNameText ) ;
121198 }
122199 getModuleNameText ( node ) ;
123- console . log ( importPaths ) ;
124200
125201 importPaths . forEach ( moduleNameText => {
126202 if ( ! moduleNameText ) {
127- console . log ( 'moduleNameText is empty' ) ;
128203 return ;
129204 }
130205 const moduleName = moduleNameText . slice ( 1 , moduleNameText . length - 1 ) ; // import 文のクォート及びダブルクォートを除去
131- console . log (
132- 'ts.resolveModuleName(moduleName, sourceFile.fileName, options, ts.sys).resolvedModule?.resolvedFileName = ' ,
133- ts . resolveModuleName ( moduleName , sourceFile . fileName , options , ts . sys )
134- . resolvedModule ?. resolvedFileName ,
135- ) ;
136206 const moduleFileFullName =
137207 ts . resolveModuleName ( moduleName , sourceFile . fileName , options , ts . sys )
138208 . resolvedModule ?. resolvedFileName ?? '' ;
@@ -142,7 +212,6 @@ function analyzeSoucreFile(
142212 : moduleFileFullName ,
143213 ) ;
144214 if ( ! moduleFilePath ) {
145- console . log ( 'moduleFilePath is empty' ) ;
146215 return ;
147216 }
148217 const toNode : Node = {
@@ -165,3 +234,43 @@ function analyzeSoucreFile(
165234 return { nodes, relations } ;
166235 } ;
167236}
237+
238+ function getVueAndTsFilePathsRecursive (
239+ dir : string ,
240+ mut_filePaths : string [ ] = [ ] ,
241+ ) : string [ ] {
242+ const files = fs . readdirSync ( dir ) ;
243+ files . forEach ( file => {
244+ const filePath = path . join ( dir , file ) ;
245+ if (
246+ fs . statSync ( filePath ) . isDirectory ( ) &&
247+ ! filePath . includes ( 'node_modules' )
248+ ) {
249+ // ディレクトリの場合は再帰的に呼び出す
250+ return getVueAndTsFilePathsRecursive ( filePath , mut_filePaths ) ;
251+ }
252+
253+ if (
254+ // ts.Extension and vue
255+ [
256+ '.ts' ,
257+ '.tsx' ,
258+ '.d.ts' ,
259+ '.js' ,
260+ '.jsx' ,
261+ '.json' ,
262+ '.tsbuildinfo' ,
263+ '.mjs' ,
264+ '.mts' ,
265+ '.d.mts' ,
266+ '.cjs' ,
267+ '.cts' ,
268+ '.d.cts' ,
269+ '.vue' ,
270+ ] . some ( ext => filePath . endsWith ( ext ) )
271+ ) {
272+ mut_filePaths . push ( filePath ) ;
273+ }
274+ } ) ;
275+ return mut_filePaths ;
276+ }
0 commit comments