-
Notifications
You must be signed in to change notification settings - Fork 392
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Provide support for type augmentation (#8534)
Allowing for the extensibility of our build-in core models helps in implementing a da- ta-driven approach for both our own libraries and 3rd party ones. As there are some well-known limitations in TypeScript around it, mentioned in those tickets: microsoft/TypeScript#9532 microsoft/TypeScript#18877 Spartacus uses additional build step for our libraries that will move augmentable models to main entry point generated by ng-packagr (eg spartacus-core.d.ts for core). Closes #7940
- Loading branch information
Showing
10 changed files
with
484 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Outputs | ||
**/*.js.map | ||
**/*.d.ts | ||
|
||
# IDEs | ||
.idea/ | ||
jsconfig.json | ||
.vscode/ | ||
|
||
# Misc | ||
node_modules/ | ||
npm-debug.log* | ||
yarn-error.log* | ||
|
||
# Mac OSX Finder files. | ||
**/.DS_Store | ||
.DS_Store |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { | ||
BuilderContext, | ||
BuilderOutput, | ||
createBuilder, | ||
} from '@angular-devkit/architect'; | ||
import { JsonObject, logging } from '@angular-devkit/core'; | ||
import { NgPackagrBuilderOptions } from '@angular-devkit/build-ng-packagr'; | ||
import { from, Observable, of } from 'rxjs'; | ||
import { switchMap } from 'rxjs/operators'; | ||
import { promises as fs } from 'fs'; | ||
import * as path from 'path'; | ||
import * as globModule from 'glob'; | ||
import { promisify } from 'util'; | ||
const glob = promisify(globModule); | ||
|
||
const DELIMITER_START = '/** AUGMENTABLE_TYPES_START */'; | ||
const DELIMITER_END = '/** AUGMENTABLE_TYPES_END */'; | ||
|
||
export default createBuilder(augmentedTypesBuilder); | ||
|
||
/** | ||
* Builder that runs default ng-packagr builder ('@angular-devkit/build-ng-packagr:build') | ||
* and performs additional post step to move augmentable types to root d.ts file. | ||
* | ||
* It's a workaround to make TS types augmentable, reference issues: | ||
* - https://github.com/microsoft/TypeScript/issues/9532 | ||
* - https://github.com/microsoft/TypeScript/issues/18877 | ||
*/ | ||
function augmentedTypesBuilder( | ||
options: NgPackagrBuilderOptions & JsonObject, | ||
context: BuilderContext | ||
): Observable<BuilderOutput> { | ||
return from(ngPackagrBuild(context, options)).pipe( | ||
switchMap((result) => | ||
result.success | ||
? from( | ||
augmentableTypesPostStep( | ||
context, | ||
options as NgPackagrBuilderOptions | ||
) | ||
) | ||
: of(result) | ||
) | ||
); | ||
} | ||
|
||
/** | ||
* Run ng packager build step as is | ||
*/ | ||
async function ngPackagrBuild( | ||
context: BuilderContext, | ||
options: NgPackagrBuilderOptions & JsonObject | ||
): Promise<BuilderOutput> { | ||
const builderRun = await context.scheduleBuilder( | ||
'@angular-devkit/build-ng-packagr:build', | ||
options | ||
); | ||
return await builderRun.result; | ||
} | ||
|
||
/** | ||
* Post build step | ||
*/ | ||
async function augmentableTypesPostStep( | ||
context: BuilderContext, | ||
options: NgPackagrBuilderOptions | ||
): Promise<BuilderOutput> { | ||
const outputPath = await getNgPackgrLibOutputPath(options.project); | ||
await propagateAugmentableTypes(outputPath, context.logger); | ||
return { success: true }; | ||
} | ||
|
||
/** | ||
* Get output directory for ng packager job | ||
* @param ngPackagerFile | ||
*/ | ||
async function getNgPackgrLibOutputPath(ngPackagerFile: string) { | ||
let ngPackageData = JSON.parse(await fs.readFile(ngPackagerFile, 'utf8')); | ||
return path.join(path.dirname(ngPackagerFile), ngPackageData.dest); | ||
} | ||
|
||
/** | ||
* Propagate augmentable types for every package.json file in the built in library | ||
*/ | ||
async function propagateAugmentableTypes( | ||
libPath: string, | ||
logger: logging.LoggerApi | ||
) { | ||
// grab all package.json files | ||
const files = await glob(libPath + '/**/package.json'); | ||
|
||
for (const packageJsonFile of files) { | ||
try { | ||
// get typings file from package.json | ||
let packageData = JSON.parse(await fs.readFile(packageJsonFile, 'utf8')); | ||
const typingsFile = packageData.typings; | ||
|
||
if (!typingsFile) { | ||
continue; | ||
} | ||
const typingsFilePath = path.join(libPath, typingsFile); | ||
let typingsFileSource = await fs.readFile(typingsFilePath, 'utf8'); | ||
|
||
// look for export from public api file | ||
const regex = /export \* from '(.+)\'/; | ||
const publicApiFile = typingsFileSource.match(regex)![1]; | ||
const apiFilePath = path.join(libPath, publicApiFile + '.d.ts'); | ||
|
||
let publicApiFileSource = await fs.readFile(apiFilePath, 'utf8'); | ||
|
||
// find augmentable types delimiter in public api file | ||
const augTypesStart = publicApiFileSource.indexOf(DELIMITER_START); | ||
|
||
if (augTypesStart === -1) { | ||
return; | ||
} | ||
|
||
const augTypesEnd = | ||
publicApiFileSource.indexOf(DELIMITER_END) + DELIMITER_END.length + 1; | ||
|
||
// extract augmentable types block | ||
const augTypes = publicApiFileSource.substr( | ||
augTypesStart, | ||
augTypesEnd - augTypesStart | ||
); | ||
// remove augmentable types block from public api file | ||
publicApiFileSource = | ||
publicApiFileSource.substr(0, augTypesStart) + | ||
publicApiFileSource.substr(augTypesEnd); | ||
|
||
// incorporate augmentable types block into typings file | ||
const firstExportPos = typingsFileSource.indexOf('export *'); | ||
typingsFileSource = | ||
typingsFileSource.substr(0, firstExportPos) + | ||
augTypes + | ||
typingsFileSource.substr(firstExportPos); | ||
|
||
// write results | ||
await fs.writeFile(apiFilePath, publicApiFileSource, 'utf8'); | ||
await fs.writeFile(typingsFilePath, typingsFileSource, 'utf8'); | ||
|
||
logger.info( | ||
'Propagated types from ' + apiFilePath + ' to ' + typingsFilePath | ||
); | ||
} catch (e) { | ||
logger.error( | ||
'Error when propagating augmentable types for ' + packageJsonFile | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"$schema": "http://json-schema.org/schema", | ||
"type": "object" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"builders": { | ||
"augmented-types": { | ||
"implementation": "./augmented-types", | ||
"schema": "./augmented-types/schema.json", | ||
"description": "Propagate typing exports to main d.ts file to make them augmentable." | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "@spartacus/build-lib", | ||
"version": "0.1.0", | ||
"description": "Angular Build Architect for Spartacus libraries", | ||
"main": "augmented-types/index.js", | ||
"builders": "builders.json", | ||
"scripts": { | ||
"build": "tsc" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "Apache-2.0", | ||
"devDependencies": { | ||
"@types/node": "^12.12.27", | ||
"ts-node": "^8.1.0", | ||
"typescript": "^3.4.3" | ||
}, | ||
"dependencies": { | ||
"@angular-devkit/architect": "0.901.7" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"compilerOptions": { | ||
"baseUrl": "tsconfig", | ||
"target": "es6", | ||
"declaration": true, | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"noEmitOnError": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitAny": true, | ||
"noImplicitThis": true, | ||
"noUnusedParameters": true, | ||
"noUnusedLocals": true, | ||
"skipDefaultLibCheck": true, | ||
"skipLibCheck": true, | ||
"sourceMap": true, | ||
"strictNullChecks": true, | ||
"types": ["node", "jasmine"] | ||
} | ||
} |
Oops, something went wrong.