|
| 1 | +const fs = require('fs'); |
| 2 | +const path = require('path'); |
| 3 | +const ts = require('typescript'); |
| 4 | +const chalk = require('chalk'); |
| 5 | + |
| 6 | +/** -------------------------------------------------------------------------------------------- */ |
| 7 | +/** -------------------------------------- ERROR HANDLING -------------------------------------- */ |
| 8 | +/** -------------------------------------------------------------------------------------------- */ |
| 9 | + |
| 10 | +/** Stores all errors that are caught during execution. */ |
| 11 | +const errors = []; |
| 12 | + |
| 13 | +/** Throws an error explaining that the given export node must be explicit. */ |
| 14 | +function throwExplicitExportError(path, content, node) { |
| 15 | + const invalidExport = content |
| 16 | + .substring(node.jsDoc ? node.jsDoc[0].end : node.pos, node.end) |
| 17 | + .trim(); |
| 18 | + errors.push( |
| 19 | + chalk.red( |
| 20 | + `ERROR ${errors.length + 1}:` + |
| 21 | + `\n File: ${path}` + |
| 22 | + `\n Line: "${invalidExport}"` + |
| 23 | + `\n Re-exports must be explicit. For example:` + |
| 24 | + `\n [x] export * from './foo';` + |
| 25 | + `\n [✓] export {MatFoo} from './foo';`, |
| 26 | + ), |
| 27 | + ); |
| 28 | +} |
| 29 | + |
| 30 | +/** Throws an error explaining that the name(s) of given export node must contain "legacy". */ |
| 31 | +function throwLegacyNamingError(path, node) { |
| 32 | + const invalidSymbol = node.exportClause.elements |
| 33 | + .map(n => n.name.escapedText) |
| 34 | + .filter(n => !n.includes('legacy')) |
| 35 | + .join('\n * '); |
| 36 | + errors.push( |
| 37 | + chalk.red( |
| 38 | + `ERROR ${errors.length + 1}:` + |
| 39 | + `\n File: ${path}` + |
| 40 | + `\n The following exported symbols do not contain 'legacy':` + |
| 41 | + `\n ${invalidSymbol}`, |
| 42 | + ), |
| 43 | + ); |
| 44 | +} |
| 45 | + |
| 46 | +/** -------------------------------------------------------------------------------------------- */ |
| 47 | +/** --------------------------------------- BEGIN SCRIPT --------------------------------------- */ |
| 48 | +/** -------------------------------------------------------------------------------------------- */ |
| 49 | + |
| 50 | +/** An array of the file paths of all public-api.ts files for legacy components. */ |
| 51 | +const paths = fs |
| 52 | + .readdirSync(path.join(path.dirname(__dirname), 'src/material')) |
| 53 | + .filter(dir => dir.startsWith('legacy-')) |
| 54 | + .filter(dir => dir !== 'legacy-prebuilt-themes' && dir !== 'legacy-core') |
| 55 | + .flatMap(dir => { |
| 56 | + const base = path.join(path.dirname(__dirname), 'src/material', dir); |
| 57 | + return [path.join(base, 'public-api.ts'), path.join(base, 'testing/public-api.ts')]; |
| 58 | + }); |
| 59 | + |
| 60 | +for (let i = 0; i < paths.length; i++) { |
| 61 | + const content = fs.readFileSync(paths[i], 'utf8'); |
| 62 | + ts.createSourceFile(paths[i], content, ts.ScriptTarget.Latest).forEachChild(child => { |
| 63 | + // todo: consider enforcing this for all public-api.ts files. |
| 64 | + if (!ts.isExportDeclaration(child)) { |
| 65 | + return; |
| 66 | + } |
| 67 | + |
| 68 | + // Do not allow wildcard forwarding of exports. |
| 69 | + // E.g. `export * from './foo';` vs `export {Foo} from './foo';`. |
| 70 | + if (!child.exportClause) { |
| 71 | + throwExplicitExportError(paths[i], content, child); |
| 72 | + return; |
| 73 | + } |
| 74 | + |
| 75 | + // Ensure all exports from public-api.ts files nested |
| 76 | + // under src/legacy-* directory contain the word "legacy". |
| 77 | + for (let j = 0; j < child.exportClause.elements.length; j++) { |
| 78 | + if (!child.exportClause.elements[j].name.escapedText.toLowerCase().includes('legacy')) { |
| 79 | + throwLegacyNamingError(paths[i], child); |
| 80 | + return; |
| 81 | + } |
| 82 | + } |
| 83 | + }); |
| 84 | +} |
| 85 | + |
| 86 | +/** -------------------------------------------------------------------------------------------- */ |
| 87 | +/** ------------------------------------- ERROR REPORTING -------------------------------------- */ |
| 88 | +/** -------------------------------------------------------------------------------------------- */ |
| 89 | + |
| 90 | +if (errors.length) { |
| 91 | + const separator = chalk.red('\n-----------------------------------------------------\n'); |
| 92 | + console.log( |
| 93 | + chalk.red(`\nPublic APIs check failed with ${errors.length} error(s)`), |
| 94 | + separator, |
| 95 | + errors.join(`${separator}`), |
| 96 | + separator, |
| 97 | + ); |
| 98 | + process.exitCode = 1; |
| 99 | +} |
0 commit comments