@@ -6,9 +6,9 @@ import { transform } from 'sucrase';
66import glob from 'tiny-glob/sync.js' ;
77import { mkdirp , rimraf } from '../utils.js' ;
88
9- /** @param {string } typescript */
10- function convert_typescript ( typescript ) {
11- const transformed = transform ( typescript , {
9+ /** @param {string } content */
10+ function convert_typescript ( content ) {
11+ const transformed = transform ( content , {
1212 transforms : [ 'typescript' ]
1313 } ) ;
1414
@@ -21,6 +21,11 @@ function convert_typescript(typescript) {
2121 } ) ;
2222}
2323
24+ /** @param {string } content */
25+ function strip_jsdoc ( content ) {
26+ return content . replace ( / \/ \* \* [ \s \S ] + ?\* \/ [ \s \n ] + / g, '' ) ;
27+ }
28+
2429/** @param {Set<string> } shared */
2530async function generate_templates ( shared ) {
2631 const templates = fs . readdirSync ( 'templates' ) ;
@@ -43,8 +48,12 @@ async function generate_templates(shared) {
4348 const meta_file = path . join ( cwd , '.meta.json' ) ;
4449 if ( ! fs . existsSync ( meta_file ) ) throw new Error ( 'Template must have a .meta.json file' ) ;
4550
46- /** @type {import('../types/internal.js').File[] } */
47- const ts = [ ] ;
51+ /** @type {Record<string, import('../types/internal.js').File[]> } */
52+ const types = {
53+ typescript : [ ] ,
54+ checkjs : [ ] ,
55+ null : [ ]
56+ } ;
4857
4958 glob ( '**/*' , { cwd, filesOnly : true , dot : true } ) . forEach ( ( name ) => {
5059 // the package.template.json thing is a bit annoying — basically we want
@@ -64,88 +73,102 @@ async function generate_templates(shared) {
6473 // ignore contents of .gitignore or .ignore
6574 if ( ! gitignore . accepts ( name ) || ! ignore . accepts ( name ) || name === '.ignore' ) return ;
6675
67- if ( / \. ( j s | t s | s v e l t e | s v e l t e \. m d ) $ / . test ( name ) ) {
76+ if ( / \. ( t s | s v e l t e ) $ / . test ( name ) ) {
6877 const contents = fs . readFileSync ( path . join ( cwd , name ) , 'utf8' ) ;
69- ts . push ( {
70- name,
71- contents
72- } ) ;
78+
79+ if ( name . endsWith ( '.d.ts' ) ) {
80+ if ( name . endsWith ( 'app.d.ts' ) ) types . checkjs . push ( { name, contents } ) ;
81+ types . typescript . push ( { name, contents } ) ;
82+ } else if ( name . endsWith ( '.ts' ) ) {
83+ const js = convert_typescript ( contents ) ;
84+
85+ types . typescript . push ( {
86+ name,
87+ contents : strip_jsdoc ( contents )
88+ } ) ;
89+
90+ types . checkjs . push ( {
91+ name : name . replace ( / \. t s $ / , '.js' ) ,
92+ contents : js
93+ } ) ;
94+
95+ types . null . push ( {
96+ name : name . replace ( / \. t s $ / , '.js' ) ,
97+ contents : strip_jsdoc ( js )
98+ } ) ;
99+ } else {
100+ // we jump through some hoops, rather than just using svelte.preprocess,
101+ // so that the output preserves the original formatting to the extent
102+ // possible (e.g. preserving double line breaks). Sucrase is the best
103+ // tool for the job because it just removes the types; Prettier then
104+ // tidies up the end result
105+ const js_contents = contents . replace (
106+ / < s c r i p t ( [ ^ > ] + ) > ( [ \s \S ] + ?) < \/ s c r i p t > / g,
107+ ( m , attrs , typescript ) => {
108+ // Sucrase assumes 'unused' imports (which _are_ used, but only
109+ // in the markup) are type imports, and strips them. This step
110+ // prevents it from drawing that conclusion
111+ const imports = [ ] ;
112+ const import_pattern = / i m p o r t ( .+ ?) f r o m / g;
113+ let import_match ;
114+ while ( ( import_match = import_pattern . exec ( typescript ) ) ) {
115+ const word_pattern = / [ a - z _ $ ] [ a - z 0 - 9 _ $ ] * / gi;
116+ let word_match ;
117+ while ( ( word_match = word_pattern . exec ( import_match [ 1 ] ) ) ) {
118+ imports . push ( word_match [ 0 ] ) ;
119+ }
120+ }
121+
122+ const suffix = `\n${ imports . join ( ',' ) } ` ;
123+
124+ const transformed = transform ( typescript + suffix , {
125+ transforms : [ 'typescript' ]
126+ } ) . code . slice ( 0 , - suffix . length ) ;
127+
128+ const contents = prettier
129+ . format ( transformed , {
130+ parser : 'babel' ,
131+ useTabs : true ,
132+ singleQuote : true ,
133+ trailingComma : 'none' ,
134+ printWidth : 100
135+ } )
136+ . trim ( )
137+ . replace ( / ^ ( .) / gm, '\t$1' ) ;
138+
139+ return `<script${ attrs . replace ( ' lang="ts"' , '' ) } >\n${ contents } \n</script>` ;
140+ }
141+ ) ;
142+
143+ types . typescript . push ( {
144+ name,
145+ contents : strip_jsdoc ( contents )
146+ } ) ;
147+
148+ types . checkjs . push ( {
149+ name,
150+ contents : js_contents
151+ } ) ;
152+
153+ types . null . push ( {
154+ name,
155+ contents : strip_jsdoc ( js_contents )
156+ } ) ;
157+ }
73158 } else {
74159 const dest = path . join ( assets , name . replace ( / ^ \. / , 'DOT-' ) ) ;
75160 mkdirp ( path . dirname ( dest ) ) ;
76161 fs . copyFileSync ( path . join ( cwd , name ) , dest ) ;
77162 }
78163 } ) ;
79164
80- /** @type {import('../types/internal.js').File[] } */
81- const js = [ ] ;
82-
83- for ( const file of ts ) {
84- // The app.d.ts file makes TS/JS aware of some ambient modules, which are
85- // also needed for JS projects if people turn on "checkJs" in their jsonfig
86- if ( file . name . endsWith ( '.d.ts' ) ) {
87- if ( file . name . endsWith ( 'app.d.ts' ) ) js . push ( file ) ;
88- } else if ( file . name . endsWith ( '.ts' ) ) {
89- js . push ( {
90- name : file . name . replace ( / \. t s $ / , '.js' ) ,
91- contents : convert_typescript ( file . contents )
92- } ) ;
93- } else if ( file . name . endsWith ( '.svelte' ) ) {
94- // we jump through some hoops, rather than just using svelte.preprocess,
95- // so that the output preserves the original formatting to the extent
96- // possible (e.g. preserving double line breaks). Sucrase is the best
97- // tool for the job because it just removes the types; Prettier then
98- // tidies up the end result
99- const contents = file . contents . replace (
100- / < s c r i p t ( [ ^ > ] + ) > ( [ \s \S ] + ?) < \/ s c r i p t > / g,
101- ( m , attrs , typescript ) => {
102- // Sucrase assumes 'unused' imports (which _are_ used, but only
103- // in the markup) are type imports, and strips them. This step
104- // prevents it from drawing that conclusion
105- const imports = [ ] ;
106- const import_pattern = / i m p o r t ( .+ ?) f r o m / g;
107- let import_match ;
108- while ( ( import_match = import_pattern . exec ( typescript ) ) ) {
109- const word_pattern = / [ a - z _ $ ] [ a - z 0 - 9 _ $ ] * / gi;
110- let word_match ;
111- while ( ( word_match = word_pattern . exec ( import_match [ 1 ] ) ) ) {
112- imports . push ( word_match [ 0 ] ) ;
113- }
114- }
115-
116- const suffix = `\n${ imports . join ( ',' ) } ` ;
117-
118- const transformed = transform ( typescript + suffix , {
119- transforms : [ 'typescript' ]
120- } ) . code . slice ( 0 , - suffix . length ) ;
121-
122- const contents = prettier
123- . format ( transformed , {
124- parser : 'babel' ,
125- useTabs : true ,
126- singleQuote : true ,
127- trailingComma : 'none' ,
128- printWidth : 100
129- } )
130- . trim ( )
131- . replace ( / ^ ( .) / gm, '\t$1' ) ;
132-
133- return `<script${ attrs . replace ( ' lang="ts"' , '' ) } >\n${ contents } \n</script>` ;
134- }
135- ) ;
136-
137- js . push ( {
138- name : file . name ,
139- contents
140- } ) ;
141- } else {
142- js . push ( file ) ;
143- }
144- }
145-
146165 fs . copyFileSync ( meta_file , `${ dir } /meta.json` ) ;
147- fs . writeFileSync ( `${ dir } /files.ts.json` , JSON . stringify ( ts , null , '\t' ) ) ;
148- fs . writeFileSync ( `${ dir } /files.js.json` , JSON . stringify ( js , null , '\t' ) ) ;
166+ fs . writeFileSync (
167+ `${ dir } /files.types=typescript.json` ,
168+ JSON . stringify ( types . typescript , null , '\t' )
169+ ) ;
170+ fs . writeFileSync ( `${ dir } /files.types=checkjs.json` , JSON . stringify ( types . checkjs , null , '\t' ) ) ;
171+ fs . writeFileSync ( `${ dir } /files.types=null.json` , JSON . stringify ( types . null , null , '\t' ) ) ;
149172 }
150173}
151174
0 commit comments