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,95 @@ 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+ fs . mkdirSync ( path . dirname ( tmpFilePath ) , { recursive : true } ) ;
86+ fs . copyFileSync ( relativeFilePath , tmpFilePath ) ;
87+ } ) ;
88+
89+ const { options, fileNames : fullFilePaths } = ts . parseJsonConfigFileContent (
90+ config ,
91+ ts . sys ,
92+ path . join ( tmpDir , relativeRootDir ) ,
93+ ) ;
94+ // rootDir を設定しない場合、 tmpDir/rootDir である場合に `rootDir/` が node についてしまう
95+ options . rootDir = path . join ( tmpDir , relativeRootDir ) ;
96+ // ↑ここまでで、ファイルを tmp にコピーし、新たな fileNames と options を生成する
97+ const program = ts . createProgram ( fullFilePaths , options ) ;
98+ function renameNode ( node : Node ) {
99+ return {
100+ ...node ,
101+ path : node . path
102+ . replace ( '.vue.ts' , '.vue' )
103+ . replace ( `${ tmpDir . slice ( 1 ) } /` , '' ) ,
104+ name : node . name
105+ . replace ( '.vue.ts' , '.vue' )
106+ . replace ( `${ tmpDir . slice ( 1 ) } /` , '' ) ,
107+ } ;
108+ }
44109 const graphs = program
45110 . getSourceFiles ( )
46111 . filter ( sourceFile => ! sourceFile . fileName . includes ( 'node_modules' ) ) // node_modules 配下のファイルは除外
47112 . filter ( piped ( getFilePath ( options ) , removeSlash , isNotMatchSomeExclude ) )
48- . map ( analyzeSoucreFile ( options ) ) ;
49-
50- return { graph : mergeGraph ( ...graphs ) , meta : { rootDir } } ;
113+ . map ( analyzeSoucreFile ( options ) )
114+ . map ( graph => {
115+ // graph においては .vue.ts ファイルを .vue に戻す
116+ return {
117+ nodes : graph . nodes . map ( renameNode ) ,
118+ relations : graph . relations . map ( relation => {
119+ return {
120+ ...relation ,
121+ from : renameNode ( relation . from ) ,
122+ to : renameNode ( relation . to ) ,
123+ } ;
124+ } ) ,
125+ } ;
126+ } ) ;
127+ return mergeGraph ( ...graphs ) ;
51128}
52129
53130function getName ( filePath : string ) {
@@ -104,10 +181,6 @@ function analyzeSoucreFile(
104181 const importPaths : ( string | undefined ) [ ] = [ ] ;
105182 function getModuleNameText ( node : ts . Node ) {
106183 if ( ts . isImportDeclaration ( node ) ) {
107- console . log (
108- 'isImportDeclaration' ,
109- node . moduleSpecifier ?. getText ( sourceFile ) ,
110- ) ;
111184 importPaths . push ( node . moduleSpecifier ?. getText ( sourceFile ) ) ;
112185 } else if ( ts . isCallExpression ( node ) ) {
113186 const text = node . getText ( sourceFile ) ;
@@ -120,19 +193,12 @@ function analyzeSoucreFile(
120193 ts . forEachChild ( node , getModuleNameText ) ;
121194 }
122195 getModuleNameText ( node ) ;
123- console . log ( importPaths ) ;
124196
125197 importPaths . forEach ( moduleNameText => {
126198 if ( ! moduleNameText ) {
127- console . log ( 'moduleNameText is empty' ) ;
128199 return ;
129200 }
130201 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- ) ;
136202 const moduleFileFullName =
137203 ts . resolveModuleName ( moduleName , sourceFile . fileName , options , ts . sys )
138204 . resolvedModule ?. resolvedFileName ?? '' ;
@@ -142,7 +208,6 @@ function analyzeSoucreFile(
142208 : moduleFileFullName ,
143209 ) ;
144210 if ( ! moduleFilePath ) {
145- console . log ( 'moduleFilePath is empty' ) ;
146211 return ;
147212 }
148213 const toNode : Node = {
@@ -165,3 +230,43 @@ function analyzeSoucreFile(
165230 return { nodes, relations } ;
166231 } ;
167232}
233+
234+ function getVueAndTsFilePathsRecursive (
235+ dir : string ,
236+ mut_filePaths : string [ ] = [ ] ,
237+ ) : string [ ] {
238+ const files = fs . readdirSync ( dir ) ;
239+ files . forEach ( file => {
240+ const filePath = path . join ( dir , file ) ;
241+ if (
242+ fs . statSync ( filePath ) . isDirectory ( ) &&
243+ ! filePath . includes ( 'node_modules' )
244+ ) {
245+ // ディレクトリの場合は再帰的に呼び出す
246+ return getVueAndTsFilePathsRecursive ( filePath , mut_filePaths ) ;
247+ }
248+
249+ if (
250+ // ts.Extension and vue
251+ [
252+ '.ts' ,
253+ '.tsx' ,
254+ '.d.ts' ,
255+ '.js' ,
256+ '.jsx' ,
257+ '.json' ,
258+ '.tsbuildinfo' ,
259+ '.mjs' ,
260+ '.mts' ,
261+ '.d.mts' ,
262+ '.cjs' ,
263+ '.cts' ,
264+ '.d.cts' ,
265+ '.vue' ,
266+ ] . some ( ext => filePath . endsWith ( ext ) )
267+ ) {
268+ mut_filePaths . push ( filePath ) ;
269+ }
270+ } ) ;
271+ return mut_filePaths ;
272+ }
0 commit comments