1212
1313const chalk = require ( "chalk" ) ;
1414const { readFileSync, watch } = require ( "fs" ) ;
15- const { join, basename, sep } = require ( "path" ) ;
15+ const { join, basename, sep, dirname } = require ( "path" ) ;
1616const readline = require ( 'readline' )
1717
1818const ts = require ( "typescript" ) ;
@@ -54,23 +54,23 @@ if (filterString === "--watch") {
5454 validateAtPaths ( toValidate ) ;
5555}
5656
57- /** @param {string[] } mdDocs */
58- function validateAtPaths ( mdDocs ) {
57+ /** @param {string[] } docs */
58+ function validateAtPaths ( docs ) {
5959 let errorReports = [ ] ;
6060
61- mdDocs . forEach ( ( docAbsPath , i ) => {
61+ docs . forEach ( ( docAbsPath , i ) => {
6262 const docPath = docAbsPath ;
6363 const filename = basename ( docPath ) ;
6464
6565 let lintFunc = undefined ;
6666
67- if ( docAbsPath . endsWith ( ".ts" ) ) {
67+ if ( docAbsPath . includes ( "typescriptlang" ) && docAbsPath . endsWith ( ".ts" ) ) {
6868 lintFunc = lintTSLanguageFile ;
6969 } else if ( docAbsPath . endsWith ( ".md" ) ) {
7070 lintFunc = lintMarkdownFile ;
7171 }
7272
73- const isLast = i === mdDocs . length - 1 ;
73+ const isLast = i === docs . length - 1 ;
7474 const suffix = isLast ? "" : ", " ;
7575
7676 if ( ! lintFunc ) {
@@ -130,15 +130,15 @@ function lintMarkdownFile(docPath) {
130130 const lang = relativePath . split ( sep ) [ 2 ] ;
131131
132132 if ( docType === "documentation" ) {
133- if ( ! greyMD . data . display ) {
133+ if ( ! greyMD . data . title ) {
134134 errors . push ( new Error ( "Did not have a 'display' property in the YML header" ) ) ;
135135 }
136136
137137 if ( greyMD . data . layout !== "docs" ) {
138138 errors . push ( new Error ( "Expected 'layout: docs' in the YML header" ) ) ;
139139 }
140140
141- if ( greyMD . data . permalink . startsWith ( "/" + lang ) ) {
141+ if ( ! greyMD . data . permalink . startsWith ( "/" + lang ) ) {
142142 errors . push ( new Error ( `Expected 'permalink:' in the YML header to start with '/${ lang } '` ) ) ;
143143 }
144144 } else if ( docType === "tsconfig" ) {
@@ -158,7 +158,7 @@ function lintMarkdownFile(docPath) {
158158
159159/** @param {string } file */
160160function lintTSLanguageFile ( file ) {
161- /** @type {{ path: string, error: Error } [] } */
161+ /** @type {Error[] } */
162162 const errors = [ ] ;
163163
164164 const content = readFileSync ( file , "utf8" ) ;
@@ -170,22 +170,50 @@ function lintTSLanguageFile(file) {
170170 ts . ScriptKind . TS
171171 ) ;
172172
173- const tooManyStatements = sourceFile . statements . length > 1 ;
174- const notDeclarationList = sourceFile . statements [ 0 ] . kind !== 232 ;
173+
174+ const filename = basename ( file , ".ts" )
175+ const lastDir = dirname ( file ) . split ( sep ) . pop ( )
176+
177+ const isRootImport = filename === lastDir
178+ if ( isRootImport ) {
179+ // This is the import for the language which pulls in all the existing messages
180+ //
181+ const notImportStatements = sourceFile . statements . filter ( f => f . kind !== 261 )
182+ const lastStatementIsDeclaration = sourceFile . statements [ 0 ] . kind !== 232 ;
183+ const onlyImportsAndOneExport = lastStatementIsDeclaration && notImportStatements . length === 1
184+
185+ if ( ! onlyImportsAndOneExport ) {
186+ errors . push ( new Error ( "A root language import can only include imports and an export called 'lang' " ) ) ;
187+ }
175188
176- if ( tooManyStatements ) {
177- errors . push ( {
178- path : file ,
179- error : new Error ( "TS files had more than one statement (e.g. more than `export const somethingCopy = { ... }` " ) ,
180- } ) ;
181- }
189+ sourceFile . statements . forEach ( s => {
190+ if ( ! ts . isImportDeclaration ( s ) ) return
191+ if ( ! s . importClause ) errors . push ( new Error ( `The import ${ s . moduleSpecifier . getText ( sourceFile ) } is not importing an object` ) ) ;
192+
193+ const allowed = [ '"react-intl"' ]
194+ const specifier = s . moduleSpecifier . getText ( sourceFile )
182195
183- if ( notDeclarationList ) {
184- errors . push ( {
185- path : file ,
186- error : new Error ( "TS files should only look like: `export const somethingCopy = { ... }` " ) ,
187- } ) ;
196+ if ( ! allowed . includes ( specifier ) && ! specifier . startsWith ( '".' ) ) {
197+ errors . push ( new Error ( `The import ${ specifier } is not allowlisted ([${ allowed . join ( ", " ) } ]) nor relative` ) ) ;
198+ }
199+
200+ } )
201+
202+ } else {
203+ // This should just be a simple lint that it only has a declaration
204+ const tooManyStatements = sourceFile . statements . length > 1 ;
205+ const notDeclarationList = sourceFile . statements . length > 0 && sourceFile . statements [ 0 ] . kind !== 232 ;
206+
207+ if ( tooManyStatements ) {
208+ errors . push ( new Error ( "TS files had more than one statement (e.g. more than `export const somethingCopy = { ... }` " ) ) ;
209+ }
210+
211+ if ( notDeclarationList ) {
212+ errors . push ( new Error ( "TS files should only look like: `export const somethingCopy = { ... }` " ) )
213+ }
188214 }
189215
190- return errors ;
216+
217+ return errors . map ( ( e ) => ( { path : file , error : e } ) ) ;
218+
191219}
0 commit comments