1- import { tokenizer } from 'acorn '
1+ import ts from '@typescript/typescript6 '
22import { DocCompileUserError } from '@/lib/uploads/documents/compile-error'
33
44export type DocumentSourceLanguage = 'javascript' | 'python'
@@ -18,31 +18,55 @@ interface SourceToken {
1818const FILE_HELPERS = new Set ( [ 'getFileBase64' , 'addImage' , 'drawImage' , 'input_path' ] )
1919
2020function * javascriptTokens ( source : string ) : Generator < SourceToken > {
21- try {
22- const tokens = tokenizer ( source , { ecmaVersion : 'latest' , allowHashBang : true } )
23- for ( let token = tokens . getToken ( ) ; token . type . label !== 'eof' ; token = tokens . getToken ( ) ) {
24- const label = token . type . label
25- if ( label === 'string' ) {
26- yield {
27- kind : 'string' ,
28- value : source . slice ( token . start + 1 , token . end - 1 ) ,
29- start : token . start + 1 ,
30- }
31- } else if ( label === 'template' ) {
32- if ( source [ token . start - 1 ] === '`' && source [ token . end ] === '`' ) {
33- yield { kind : 'string' , value : source . slice ( token . start , token . end ) , start : token . start }
34- }
35- } else {
36- yield {
37- kind : label === 'name' ? 'word' : 'punctuation' ,
38- value : source . slice ( token . start , token . end ) ,
39- start : token . start ,
40- }
21+ const fileName = 'document.js'
22+ const sourceFile = ts . createSourceFile (
23+ fileName ,
24+ source ,
25+ ts . ScriptTarget . Latest ,
26+ true ,
27+ ts . ScriptKind . JS
28+ )
29+ const program = ts . createProgram (
30+ [ fileName ] ,
31+ { allowJs : true , noLib : true , noResolve : true } ,
32+ {
33+ getSourceFile : ( name ) => ( name === fileName ? sourceFile : undefined ) ,
34+ getDefaultLibFileName : ( ) => '' ,
35+ writeFile : ( ) => { } ,
36+ getCurrentDirectory : ( ) => '' ,
37+ getDirectories : ( ) => [ ] ,
38+ fileExists : ( name ) => name === fileName ,
39+ readFile : ( name ) => ( name === fileName ? source : undefined ) ,
40+ getCanonicalFileName : ( name ) => name ,
41+ useCaseSensitiveFileNames : ( ) => true ,
42+ getNewLine : ( ) => '\n' ,
43+ }
44+ )
45+ const diagnostic = program . getSyntacticDiagnostics ( sourceFile ) [ 0 ]
46+ if ( diagnostic ) {
47+ throw new DocCompileUserError ( ts . flattenDiagnosticMessageText ( diagnostic . messageText , '\n' ) )
48+ }
49+
50+ const pending : ts . Node [ ] = [ sourceFile ]
51+ while ( pending . length > 0 ) {
52+ const node = pending . pop ( )
53+ if ( ! node ) break
54+ if ( ts . isStringLiteralLike ( node ) ) {
55+ const start = node . getStart ( sourceFile ) + 1
56+ yield { kind : 'string' , value : source . slice ( start , node . end - 1 ) , start }
57+ continue
58+ }
59+ const children = node . getChildren ( sourceFile )
60+ if ( children . length > 0 ) {
61+ for ( let index = children . length - 1 ; index >= 0 ; index -- ) pending . push ( children [ index ] )
62+ } else {
63+ const start = node . getStart ( sourceFile )
64+ yield {
65+ kind : ts . isIdentifier ( node ) ? 'word' : 'punctuation' ,
66+ value : source . slice ( start , node . end ) ,
67+ start,
4168 }
4269 }
43- } catch ( error ) {
44- if ( error instanceof SyntaxError ) throw new DocCompileUserError ( error . message )
45- throw error
4670 }
4771}
4872
0 commit comments