feat(angular): add tailwind support when building publishable libraries (#7285)
This commit is contained in:
parent
2dd4299056
commit
a00f8e0e5b
@ -184,7 +184,7 @@
|
|||||||
"minimatch": "3.0.4",
|
"minimatch": "3.0.4",
|
||||||
"next": "^11.1.2",
|
"next": "^11.1.2",
|
||||||
"next-sitemap": "^1.6.108",
|
"next-sitemap": "^1.6.108",
|
||||||
"ng-packagr": "~12.2.0",
|
"ng-packagr": "~12.2.3",
|
||||||
"ngrx-store-freeze": "0.2.4",
|
"ngrx-store-freeze": "0.2.4",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
@ -193,6 +193,7 @@
|
|||||||
"parse5": "4.0.0",
|
"parse5": "4.0.0",
|
||||||
"postcss": "8.3.0",
|
"postcss": "8.3.0",
|
||||||
"postcss-import": "14.0.2",
|
"postcss-import": "14.0.2",
|
||||||
|
"postcss-url": "^10.1.1",
|
||||||
"precise-commits": "1.0.2",
|
"precise-commits": "1.0.2",
|
||||||
"prettier": "2.3.2",
|
"prettier": "2.3.2",
|
||||||
"pretty-quick": "^3.1.0",
|
"pretty-quick": "^3.1.0",
|
||||||
|
|||||||
@ -90,6 +90,12 @@
|
|||||||
"version": "12.9.0",
|
"version": "12.9.0",
|
||||||
"description": "Fixes invalid importPaths for buildable and publishable libs.",
|
"description": "Fixes invalid importPaths for buildable and publishable libs.",
|
||||||
"factory": "./src/migrations/update-12-9-0/update-invalid-import-paths"
|
"factory": "./src/migrations/update-12-9-0/update-invalid-import-paths"
|
||||||
|
},
|
||||||
|
"add-postcss-import": {
|
||||||
|
"cli": "nx",
|
||||||
|
"version": "13.0.0-beta.1",
|
||||||
|
"description": "Adds postcss-import package if ng-packagr is already installed.",
|
||||||
|
"factory": "./src/migrations/update-13-0-0/add-postcss-import"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageJsonUpdates": {
|
"packageJsonUpdates": {
|
||||||
@ -880,6 +886,15 @@
|
|||||||
"alwaysAddToPackageJson": false
|
"alwaysAddToPackageJson": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"13.0.0": {
|
||||||
|
"version": "13.0.0-beta.1",
|
||||||
|
"packages": {
|
||||||
|
"ng-packagr": {
|
||||||
|
"version": "~12.2.3",
|
||||||
|
"alwaysAddToPackageJson": false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,240 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Added the filePath parameter to the cache key.
|
||||||
|
* - Added PostCSS plugins needed to support TailwindCSS.
|
||||||
|
* - Added watch mode parameter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as browserslist from 'browserslist';
|
||||||
|
import * as cacache from 'cacache';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
import * as findCacheDirectory from 'find-cache-dir';
|
||||||
|
import { EsbuildExecutor } from 'ng-packagr/lib/esbuild/esbuild-executor';
|
||||||
|
import { readFile } from 'ng-packagr/lib/utils/fs';
|
||||||
|
import * as log from 'ng-packagr/lib/utils/log';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
import { extname } from 'path';
|
||||||
|
import postcss from 'postcss';
|
||||||
|
import * as postcssPresetEnv from 'postcss-preset-env';
|
||||||
|
import * as postcssUrl from 'postcss-url';
|
||||||
|
import { getTailwindPostCssPluginsIfPresent } from '../../utilities/tailwindcss';
|
||||||
|
|
||||||
|
export enum CssUrl {
|
||||||
|
inline = 'inline',
|
||||||
|
none = 'none',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Result {
|
||||||
|
css: string;
|
||||||
|
warnings: string[];
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachePath = findCacheDirectory({ name: 'ng-packagr-styles' }) || tmpdir();
|
||||||
|
const ngPackagrVersion = require('ng-packagr/package.json').version;
|
||||||
|
|
||||||
|
export class StylesheetProcessor {
|
||||||
|
private browserslistData: string[];
|
||||||
|
private postCssProcessor: ReturnType<typeof postcss>;
|
||||||
|
private esbuild = new EsbuildExecutor();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly basePath: string,
|
||||||
|
private readonly cssUrl?: CssUrl,
|
||||||
|
private readonly styleIncludePaths?: string[],
|
||||||
|
private readonly watch?: boolean
|
||||||
|
) {
|
||||||
|
this.browserslistData = browserslist(undefined, { path: this.basePath });
|
||||||
|
this.postCssProcessor = this.createPostCssPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
async process(filePath: string): Promise<string> {
|
||||||
|
const content = await readFile(filePath, 'utf8');
|
||||||
|
let key: string | undefined;
|
||||||
|
|
||||||
|
if (!content.includes('@import') && !content.includes('@use')) {
|
||||||
|
// No transitive deps, we can cache more aggressively.
|
||||||
|
key = generateKey(content, this.browserslistData, filePath);
|
||||||
|
const result = await readCacheEntry(cachePath, key);
|
||||||
|
if (result) {
|
||||||
|
result.warnings.forEach((msg) => log.warn(msg));
|
||||||
|
return result.css;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render pre-processor language (sass, styl, less)
|
||||||
|
const renderedCss = await this.renderCss(filePath, content);
|
||||||
|
|
||||||
|
// We cannot cache CSS re-rendering phase, because a transitive dependency via (@import) can case different CSS output.
|
||||||
|
// Example a change in a mixin or SCSS variable.
|
||||||
|
if (!key) {
|
||||||
|
key = generateKey(renderedCss, this.browserslistData, filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedResult = await readCacheEntry(cachePath, key);
|
||||||
|
if (cachedResult) {
|
||||||
|
cachedResult.warnings.forEach((msg) => log.warn(msg));
|
||||||
|
return cachedResult.css;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render postcss (autoprefixing and friends)
|
||||||
|
const result = await this.postCssProcessor.process(renderedCss, {
|
||||||
|
from: filePath,
|
||||||
|
to: filePath.replace(extname(filePath), '.css'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const warnings = result.warnings().map((w) => w.toString());
|
||||||
|
const { code, warnings: esBuildWarnings } = await this.esbuild.transform(
|
||||||
|
result.css,
|
||||||
|
{
|
||||||
|
loader: 'css',
|
||||||
|
minify: true,
|
||||||
|
sourcefile: filePath,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (esBuildWarnings.length > 0) {
|
||||||
|
warnings.push(
|
||||||
|
...(await this.esbuild.formatMessages(esBuildWarnings, {
|
||||||
|
kind: 'warning',
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to cache
|
||||||
|
await cacache.put(
|
||||||
|
cachePath,
|
||||||
|
key,
|
||||||
|
JSON.stringify({
|
||||||
|
css: code,
|
||||||
|
warnings,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
warnings.forEach((msg) => log.warn(msg));
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPostCssPlugins(): ReturnType<typeof postcss> {
|
||||||
|
const postCssPlugins = [];
|
||||||
|
if (this.cssUrl !== CssUrl.none) {
|
||||||
|
postCssPlugins.push(postcssUrl({ url: this.cssUrl }));
|
||||||
|
}
|
||||||
|
|
||||||
|
postCssPlugins.push(
|
||||||
|
...getTailwindPostCssPluginsIfPresent(
|
||||||
|
this.basePath,
|
||||||
|
this.styleIncludePaths,
|
||||||
|
this.watch
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
postCssPlugins.push(
|
||||||
|
postcssPresetEnv({
|
||||||
|
browsers: this.browserslistData,
|
||||||
|
autoprefixer: true,
|
||||||
|
stage: 3,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return postcss(postCssPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async renderCss(filePath: string, css: string): Promise<string> {
|
||||||
|
const ext = extname(filePath);
|
||||||
|
|
||||||
|
switch (ext) {
|
||||||
|
case '.sass':
|
||||||
|
case '.scss': {
|
||||||
|
/*
|
||||||
|
* Please be aware of the few differences in behaviour https://github.com/sass/dart-sass/blob/master/README.md#behavioral-differences-from-ruby-sass
|
||||||
|
* By default `npm install` will install sass.
|
||||||
|
* To use node-sass you need to use:
|
||||||
|
* Npm:
|
||||||
|
* `npm install node-sass --save-dev`
|
||||||
|
* Yarn:
|
||||||
|
* `yarn add node-sass --dev`
|
||||||
|
*/
|
||||||
|
let sassCompiler: any | undefined;
|
||||||
|
try {
|
||||||
|
sassCompiler = require('node-sass'); // Check if node-sass is explicitly included.
|
||||||
|
} catch {
|
||||||
|
sassCompiler = await import('sass');
|
||||||
|
}
|
||||||
|
|
||||||
|
return sassCompiler
|
||||||
|
.renderSync({
|
||||||
|
file: filePath,
|
||||||
|
data: css,
|
||||||
|
indentedSyntax: '.sass' === ext,
|
||||||
|
importer: await import('node-sass-tilde-importer'),
|
||||||
|
includePaths: this.styleIncludePaths,
|
||||||
|
})
|
||||||
|
.css.toString();
|
||||||
|
}
|
||||||
|
case '.less': {
|
||||||
|
const { css: content } = await (
|
||||||
|
await import('less')
|
||||||
|
).render(css, {
|
||||||
|
filename: filePath,
|
||||||
|
javascriptEnabled: true,
|
||||||
|
paths: this.styleIncludePaths,
|
||||||
|
});
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
case '.styl':
|
||||||
|
case '.stylus': {
|
||||||
|
const stylus = await import('stylus');
|
||||||
|
|
||||||
|
return (
|
||||||
|
stylus(css)
|
||||||
|
// add paths for resolve
|
||||||
|
.set('paths', [
|
||||||
|
this.basePath,
|
||||||
|
'.',
|
||||||
|
...this.styleIncludePaths,
|
||||||
|
'node_modules',
|
||||||
|
])
|
||||||
|
// add support for resolving plugins from node_modules
|
||||||
|
.set('filename', filePath)
|
||||||
|
// turn on url resolver in stylus, same as flag --resolve-url
|
||||||
|
.set('resolve url', true)
|
||||||
|
.define('url', stylus.resolver(undefined))
|
||||||
|
.render()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case '.css':
|
||||||
|
default:
|
||||||
|
return css;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateKey(
|
||||||
|
content: string,
|
||||||
|
browserslistData: string[],
|
||||||
|
filePath: string
|
||||||
|
): string {
|
||||||
|
return createHash('sha1')
|
||||||
|
.update(ngPackagrVersion)
|
||||||
|
.update(content)
|
||||||
|
.update(browserslistData.join(''))
|
||||||
|
.update(filePath)
|
||||||
|
.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readCacheEntry(
|
||||||
|
cachePath: string,
|
||||||
|
key: string
|
||||||
|
): Promise<Result | undefined> {
|
||||||
|
const entry = await cacache.get.info(cachePath, key);
|
||||||
|
if (entry) {
|
||||||
|
return JSON.parse(await readFile(entry.path, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Use our own compileNgcTransformFactory instead of the one provided by ng-packagr.
|
||||||
|
* - Use NX_STYLESHEET_PROCESSOR instead of STYLESHEET_PROCESSOR.
|
||||||
|
* - Use NX_STYLESHEET_PROCESSOR_TOKEN instead of STYLESHEET_PROCESSOR_TOKEN.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { InjectionToken, Provider } from 'injection-js';
|
||||||
|
import { Transform } from 'ng-packagr/lib/graph/transform';
|
||||||
|
import {
|
||||||
|
provideTransform,
|
||||||
|
TransformProvider,
|
||||||
|
} from 'ng-packagr/lib/graph/transform.di';
|
||||||
|
import { OPTIONS_TOKEN } from 'ng-packagr/lib/ng-package/options.di';
|
||||||
|
import {
|
||||||
|
NX_STYLESHEET_PROCESSOR,
|
||||||
|
NX_STYLESHEET_PROCESSOR_TOKEN,
|
||||||
|
} from '../../styles/stylesheet-processor.di';
|
||||||
|
import { compileNgcTransformFactory } from './compile-ngc.transform';
|
||||||
|
|
||||||
|
export const NX_COMPILE_NGC_TOKEN = new InjectionToken<Transform>(
|
||||||
|
`nx.v1.compileNgcTransform`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NX_COMPILE_NGC_TRANSFORM: TransformProvider = provideTransform({
|
||||||
|
provide: NX_COMPILE_NGC_TOKEN,
|
||||||
|
useFactory: compileNgcTransformFactory,
|
||||||
|
deps: [NX_STYLESHEET_PROCESSOR_TOKEN, OPTIONS_TOKEN],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NX_COMPILE_NGC_PROVIDERS: Provider[] = [
|
||||||
|
NX_STYLESHEET_PROCESSOR,
|
||||||
|
NX_COMPILE_NGC_TRANSFORM,
|
||||||
|
];
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Use our own StylesheetProcessor files instead of the ones provide by ng-packagr.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Transform,
|
||||||
|
transformFromPromise,
|
||||||
|
} from 'ng-packagr/lib/graph/transform';
|
||||||
|
import * as ivy from 'ng-packagr/lib/ivy';
|
||||||
|
import {
|
||||||
|
EntryPointNode,
|
||||||
|
isEntryPoint,
|
||||||
|
isEntryPointInProgress,
|
||||||
|
} from 'ng-packagr/lib/ng-package/nodes';
|
||||||
|
import { NgPackagrOptions } from 'ng-packagr/lib/ng-package/options.di';
|
||||||
|
import { compileSourceFiles } from 'ng-packagr/lib/ngc/compile-source-files';
|
||||||
|
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
|
||||||
|
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
||||||
|
import * as ora from 'ora';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
import { StylesheetProcessor as IvyStylesheetProcessor } from '../../ivy/styles/stylesheet-processor';
|
||||||
|
import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor';
|
||||||
|
|
||||||
|
function isEnabled(variable: string | undefined): variable is string {
|
||||||
|
return (
|
||||||
|
typeof variable === 'string' &&
|
||||||
|
(variable === '1' || variable.toLowerCase() === 'true')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compileNgcTransformFactory = (
|
||||||
|
StylesheetProcessor: typeof StylesheetProcessorClass,
|
||||||
|
options: NgPackagrOptions
|
||||||
|
): Transform => {
|
||||||
|
return transformFromPromise(async (graph) => {
|
||||||
|
const spinner = ora({
|
||||||
|
hideCursor: false,
|
||||||
|
discardStdin: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const entryPoint: EntryPointNode = graph.find(isEntryPointInProgress());
|
||||||
|
const entryPoints: EntryPointNode[] = graph.filter(isEntryPoint);
|
||||||
|
// Add paths mappings for dependencies
|
||||||
|
const tsConfig = setDependenciesTsConfigPaths(
|
||||||
|
entryPoint.data.tsConfig,
|
||||||
|
entryPoints
|
||||||
|
);
|
||||||
|
|
||||||
|
// Compile TypeScript sources
|
||||||
|
const { esm2015, declarations } = entryPoint.data.destinationFiles;
|
||||||
|
const { basePath, cssUrl, styleIncludePaths } =
|
||||||
|
entryPoint.data.entryPoint;
|
||||||
|
const { moduleResolutionCache, ngccProcessingCache } = entryPoint.cache;
|
||||||
|
|
||||||
|
let ngccProcessor: NgccProcessor | undefined;
|
||||||
|
if (tsConfig.options.enableIvy !== false) {
|
||||||
|
spinner.start(
|
||||||
|
`Compiling with Angular sources in Ivy ${
|
||||||
|
tsConfig.options.compilationMode || 'full'
|
||||||
|
} compilation mode.`
|
||||||
|
);
|
||||||
|
ngccProcessor = new NgccProcessor(
|
||||||
|
ngccProcessingCache,
|
||||||
|
tsConfig.project,
|
||||||
|
tsConfig.options,
|
||||||
|
entryPoints
|
||||||
|
);
|
||||||
|
if (!entryPoint.data.entryPoint.isSecondaryEntryPoint) {
|
||||||
|
// Only run the async version of NGCC during the primary entrypoint processing.
|
||||||
|
await ngccProcessor.process();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spinner.start(
|
||||||
|
`Compiling with Angular in legacy View Engine compilation mode.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
tsConfig.options.enableIvy !== false &&
|
||||||
|
!isEnabled(process.env['NG_BUILD_LIB_LEGACY'])
|
||||||
|
) {
|
||||||
|
entryPoint.cache.stylesheetProcessor ??= new IvyStylesheetProcessor(
|
||||||
|
basePath,
|
||||||
|
cssUrl,
|
||||||
|
styleIncludePaths
|
||||||
|
) as any;
|
||||||
|
|
||||||
|
await ivy.compileSourceFiles(
|
||||||
|
graph,
|
||||||
|
tsConfig,
|
||||||
|
moduleResolutionCache,
|
||||||
|
{
|
||||||
|
outDir: path.dirname(esm2015),
|
||||||
|
declarationDir: path.dirname(declarations),
|
||||||
|
declaration: true,
|
||||||
|
target: ts.ScriptTarget.ES2015,
|
||||||
|
},
|
||||||
|
entryPoint.cache.stylesheetProcessor as any,
|
||||||
|
ngccProcessor,
|
||||||
|
options.watch
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
entryPoint.cache.stylesheetProcessor ??= new StylesheetProcessor(
|
||||||
|
basePath,
|
||||||
|
cssUrl,
|
||||||
|
styleIncludePaths,
|
||||||
|
options.watch
|
||||||
|
) as any;
|
||||||
|
await compileSourceFiles(
|
||||||
|
graph,
|
||||||
|
tsConfig,
|
||||||
|
moduleResolutionCache,
|
||||||
|
entryPoint.cache.stylesheetProcessor as any,
|
||||||
|
{
|
||||||
|
outDir: path.dirname(esm2015),
|
||||||
|
declarationDir: path.dirname(declarations),
|
||||||
|
declaration: true,
|
||||||
|
target: ts.ScriptTarget.ES2015,
|
||||||
|
},
|
||||||
|
ngccProcessor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.succeed();
|
||||||
|
return graph;
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Use NX_COMPILE_NGC_TOKEN instead of COMPILE_NGC_TOKEN.
|
||||||
|
* - Use NX_COMPILE_NGC_PROVIDERS instead of COMPILE_NGC_PROVIDERS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { InjectionToken, Provider } from 'injection-js';
|
||||||
|
import { Transform } from 'ng-packagr/lib/graph/transform';
|
||||||
|
import {
|
||||||
|
provideTransform,
|
||||||
|
TransformProvider,
|
||||||
|
} from 'ng-packagr/lib/graph/transform.di';
|
||||||
|
import { entryPointTransformFactory } from 'ng-packagr/lib/ng-package/entry-point/entry-point.transform';
|
||||||
|
import {
|
||||||
|
WRITE_BUNDLES_TRANSFORM,
|
||||||
|
WRITE_BUNDLES_TRANSFORM_TOKEN,
|
||||||
|
} from 'ng-packagr/lib/ng-package/entry-point/write-bundles.di';
|
||||||
|
import {
|
||||||
|
WRITE_PACKAGE_TRANSFORM,
|
||||||
|
WRITE_PACKAGE_TRANSFORM_TOKEN,
|
||||||
|
} from 'ng-packagr/lib/ng-package/entry-point/write-package.di';
|
||||||
|
import {
|
||||||
|
NX_COMPILE_NGC_PROVIDERS,
|
||||||
|
NX_COMPILE_NGC_TOKEN,
|
||||||
|
} from './compile-ngc.di';
|
||||||
|
|
||||||
|
export const NX_ENTRY_POINT_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
||||||
|
`nx.v1.entryPointTransform`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NX_ENTRY_POINT_TRANSFORM: TransformProvider = provideTransform({
|
||||||
|
provide: NX_ENTRY_POINT_TRANSFORM_TOKEN,
|
||||||
|
useFactory: entryPointTransformFactory,
|
||||||
|
deps: [
|
||||||
|
NX_COMPILE_NGC_TOKEN,
|
||||||
|
WRITE_BUNDLES_TRANSFORM_TOKEN,
|
||||||
|
WRITE_PACKAGE_TRANSFORM_TOKEN,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NX_ENTRY_POINT_PROVIDERS: Provider[] = [
|
||||||
|
NX_ENTRY_POINT_TRANSFORM,
|
||||||
|
...NX_COMPILE_NGC_PROVIDERS,
|
||||||
|
WRITE_BUNDLES_TRANSFORM,
|
||||||
|
WRITE_PACKAGE_TRANSFORM,
|
||||||
|
];
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Use NX_ENTRY_POINT_TRANSFORM_TOKEN instead of ENTRY_POINT_TRANSFORM_TOKEN.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { InjectionToken, Provider } from 'injection-js';
|
||||||
|
import { Transform } from 'ng-packagr/lib/graph/transform';
|
||||||
|
import {
|
||||||
|
provideTransform,
|
||||||
|
TransformProvider,
|
||||||
|
} from 'ng-packagr/lib/graph/transform.di';
|
||||||
|
import {
|
||||||
|
ANALYSE_SOURCES_TOKEN,
|
||||||
|
ANALYSE_SOURCES_TRANSFORM,
|
||||||
|
} from 'ng-packagr/lib/ng-package/entry-point/analyse-sources.di';
|
||||||
|
import {
|
||||||
|
INIT_TS_CONFIG_TOKEN,
|
||||||
|
INIT_TS_CONFIG_TRANSFORM,
|
||||||
|
provideTsConfig,
|
||||||
|
} from 'ng-packagr/lib/ng-package/entry-point/init-tsconfig.di';
|
||||||
|
import {
|
||||||
|
DEFAULT_OPTIONS_PROVIDER,
|
||||||
|
OPTIONS_TOKEN,
|
||||||
|
} from 'ng-packagr/lib/ng-package/options.di';
|
||||||
|
import { packageTransformFactory } from 'ng-packagr/lib/ng-package/package.transform';
|
||||||
|
import { PROJECT_TOKEN } from 'ng-packagr/lib/project.di';
|
||||||
|
import { NX_ENTRY_POINT_TRANSFORM_TOKEN } from './entry-point/entry-point.di';
|
||||||
|
|
||||||
|
export const NX_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
||||||
|
`nx.v1.packageTransform`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NX_PACKAGE_TRANSFORM: TransformProvider = provideTransform({
|
||||||
|
provide: NX_PACKAGE_TRANSFORM_TOKEN,
|
||||||
|
useFactory: packageTransformFactory,
|
||||||
|
deps: [
|
||||||
|
PROJECT_TOKEN,
|
||||||
|
OPTIONS_TOKEN,
|
||||||
|
INIT_TS_CONFIG_TOKEN,
|
||||||
|
ANALYSE_SOURCES_TOKEN,
|
||||||
|
NX_ENTRY_POINT_TRANSFORM_TOKEN,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NX_PACKAGE_PROVIDERS: Provider[] = [
|
||||||
|
NX_PACKAGE_TRANSFORM,
|
||||||
|
DEFAULT_OPTIONS_PROVIDER,
|
||||||
|
provideTsConfig(),
|
||||||
|
INIT_TS_CONFIG_TRANSFORM,
|
||||||
|
ANALYSE_SOURCES_TRANSFORM,
|
||||||
|
];
|
||||||
@ -0,0 +1,250 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Added the filePath parameter to the cache key.
|
||||||
|
* - Added PostCSS plugins needed to support TailwindCSS.
|
||||||
|
* - Added watch mode option.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as cacache from 'cacache';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
import { EsbuildExecutor } from 'ng-packagr/lib/esbuild/esbuild-executor';
|
||||||
|
import { readFile } from 'ng-packagr/lib/utils/fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import postcss, { LazyResult } from 'postcss';
|
||||||
|
import * as postcssPresetEnv from 'postcss-preset-env';
|
||||||
|
import * as postcssUrl from 'postcss-url';
|
||||||
|
import { parentPort } from 'worker_threads';
|
||||||
|
import { getTailwindPostCssPluginsIfPresent } from '../utilities/tailwindcss';
|
||||||
|
import { CssUrl, WorkerOptions, WorkerResult } from './stylesheet-processor';
|
||||||
|
|
||||||
|
const ngPackagrVersion = require('ng-packagr/package.json').version;
|
||||||
|
|
||||||
|
async function processCss({
|
||||||
|
filePath,
|
||||||
|
browserslistData,
|
||||||
|
cssUrl,
|
||||||
|
styleIncludePaths,
|
||||||
|
basePath,
|
||||||
|
cachePath,
|
||||||
|
alwaysUseWasm,
|
||||||
|
watch,
|
||||||
|
}: WorkerOptions): Promise<WorkerResult> {
|
||||||
|
const esbuild = new EsbuildExecutor(alwaysUseWasm);
|
||||||
|
const content = await readFile(filePath, 'utf8');
|
||||||
|
let key: string | undefined;
|
||||||
|
|
||||||
|
if (!content.includes('@import') && !content.includes('@use')) {
|
||||||
|
// No transitive deps, we can cache more aggressively.
|
||||||
|
key = generateKey(content, browserslistData, filePath);
|
||||||
|
const result = await readCacheEntry(cachePath, key);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render pre-processor language (sass, styl, less)
|
||||||
|
const renderedCss = await renderCss(
|
||||||
|
filePath,
|
||||||
|
content,
|
||||||
|
basePath,
|
||||||
|
styleIncludePaths
|
||||||
|
);
|
||||||
|
|
||||||
|
// We cannot cache CSS re-rendering phase, because a transitive dependency via (@import) can case different CSS output.
|
||||||
|
// Example a change in a mixin or SCSS variable.
|
||||||
|
if (!key) {
|
||||||
|
key = generateKey(renderedCss, browserslistData, filePath);
|
||||||
|
|
||||||
|
const cachedResult = await readCacheEntry(cachePath, key);
|
||||||
|
if (cachedResult) {
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render postcss (autoprefixing and friends)
|
||||||
|
const result = await optimizeCss(
|
||||||
|
filePath,
|
||||||
|
renderedCss,
|
||||||
|
browserslistData,
|
||||||
|
basePath,
|
||||||
|
styleIncludePaths,
|
||||||
|
cssUrl,
|
||||||
|
watch
|
||||||
|
);
|
||||||
|
const warnings = result.warnings().map((w) => w.toString());
|
||||||
|
|
||||||
|
const { code, warnings: esBuildWarnings } = await esbuild.transform(
|
||||||
|
result.css,
|
||||||
|
{
|
||||||
|
loader: 'css',
|
||||||
|
minify: true,
|
||||||
|
sourcefile: filePath,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (esBuildWarnings.length > 0) {
|
||||||
|
warnings.push(
|
||||||
|
...(await esbuild.formatMessages(esBuildWarnings, { kind: 'warning' }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to cache
|
||||||
|
await cacache.put(
|
||||||
|
cachePath,
|
||||||
|
key,
|
||||||
|
JSON.stringify({
|
||||||
|
css: code,
|
||||||
|
warnings,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
css: code,
|
||||||
|
warnings,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderCss(
|
||||||
|
filePath: string,
|
||||||
|
css: string,
|
||||||
|
basePath: string,
|
||||||
|
styleIncludePaths?: string[]
|
||||||
|
): Promise<string> {
|
||||||
|
const ext = path.extname(filePath);
|
||||||
|
|
||||||
|
switch (ext) {
|
||||||
|
case '.sass':
|
||||||
|
case '.scss': {
|
||||||
|
/*
|
||||||
|
* Please be aware of the few differences in behaviour https://github.com/sass/dart-sass/blob/master/README.md#behavioral-differences-from-ruby-sass
|
||||||
|
* By default `npm install` will install sass.
|
||||||
|
* To use node-sass you need to use:
|
||||||
|
* Npm:
|
||||||
|
* `npm install node-sass --save-dev`
|
||||||
|
* Yarn:
|
||||||
|
* `yarn add node-sass --dev`
|
||||||
|
*/
|
||||||
|
let sassCompiler: any | undefined;
|
||||||
|
try {
|
||||||
|
sassCompiler = require('node-sass'); // Check if node-sass is explicitly included.
|
||||||
|
} catch {
|
||||||
|
sassCompiler = await import('sass');
|
||||||
|
}
|
||||||
|
|
||||||
|
return sassCompiler
|
||||||
|
.renderSync({
|
||||||
|
file: filePath,
|
||||||
|
data: css,
|
||||||
|
indentedSyntax: '.sass' === ext,
|
||||||
|
importer: await import('node-sass-tilde-importer'),
|
||||||
|
includePaths: styleIncludePaths,
|
||||||
|
})
|
||||||
|
.css.toString();
|
||||||
|
}
|
||||||
|
case '.less': {
|
||||||
|
const { css: content } = await (
|
||||||
|
await import('less')
|
||||||
|
).render(css, {
|
||||||
|
filename: filePath,
|
||||||
|
javascriptEnabled: true,
|
||||||
|
paths: styleIncludePaths,
|
||||||
|
});
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
case '.styl':
|
||||||
|
case '.stylus': {
|
||||||
|
const stylus = await import('stylus');
|
||||||
|
|
||||||
|
return (
|
||||||
|
stylus(css)
|
||||||
|
// add paths for resolve
|
||||||
|
.set('paths', [basePath, '.', ...styleIncludePaths, 'node_modules'])
|
||||||
|
// add support for resolving plugins from node_modules
|
||||||
|
.set('filename', filePath)
|
||||||
|
// turn on url resolver in stylus, same as flag --resolve-url
|
||||||
|
.set('resolve url', true)
|
||||||
|
.define('url', stylus.resolver(undefined))
|
||||||
|
.render()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case '.css':
|
||||||
|
default:
|
||||||
|
return css;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function optimizeCss(
|
||||||
|
filePath: string,
|
||||||
|
css: string,
|
||||||
|
browsers: string[],
|
||||||
|
basePath: string,
|
||||||
|
includePaths?: string[],
|
||||||
|
cssUrl?: CssUrl,
|
||||||
|
watch?: boolean
|
||||||
|
): LazyResult {
|
||||||
|
const postCssPlugins = [];
|
||||||
|
|
||||||
|
if (cssUrl !== CssUrl.none) {
|
||||||
|
postCssPlugins.push(postcssUrl({ url: cssUrl }));
|
||||||
|
}
|
||||||
|
|
||||||
|
postCssPlugins.push(
|
||||||
|
...getTailwindPostCssPluginsIfPresent(basePath, includePaths, watch)
|
||||||
|
);
|
||||||
|
|
||||||
|
postCssPlugins.push(
|
||||||
|
postcssPresetEnv({
|
||||||
|
browsers,
|
||||||
|
autoprefixer: true,
|
||||||
|
stage: 3,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return postcss(postCssPlugins).process(css, {
|
||||||
|
from: filePath,
|
||||||
|
to: filePath.replace(path.extname(filePath), '.css'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateKey(
|
||||||
|
content: string,
|
||||||
|
browserslistData: string[],
|
||||||
|
filePath: string
|
||||||
|
): string {
|
||||||
|
return createHash('sha1')
|
||||||
|
.update(ngPackagrVersion)
|
||||||
|
.update(content)
|
||||||
|
.update(browserslistData.join(''))
|
||||||
|
.update(filePath)
|
||||||
|
.digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readCacheEntry(
|
||||||
|
cachePath: string,
|
||||||
|
key: string
|
||||||
|
): Promise<WorkerResult | undefined> {
|
||||||
|
const entry = await cacache.get.info(cachePath, key);
|
||||||
|
if (entry) {
|
||||||
|
return JSON.parse(await readFile(entry.path, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
parentPort.on('message', async ({ signal, port, workerOptions }) => {
|
||||||
|
try {
|
||||||
|
const result = await processCss(workerOptions);
|
||||||
|
port.postMessage({ ...result });
|
||||||
|
} catch (error) {
|
||||||
|
port.postMessage({ error: error.message });
|
||||||
|
} finally {
|
||||||
|
// Change the value of signal[0] to 1
|
||||||
|
Atomics.add(signal, 0, 1);
|
||||||
|
// Unlock the main thread
|
||||||
|
Atomics.notify(signal, 0);
|
||||||
|
port.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Use our own StylesheetProcessor instead of the one provided by ng-packagr.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { FactoryProvider, InjectionToken } from 'injection-js';
|
||||||
|
import { StylesheetProcessor } from './stylesheet-processor';
|
||||||
|
|
||||||
|
export const NX_STYLESHEET_PROCESSOR_TOKEN =
|
||||||
|
new InjectionToken<StylesheetProcessor>(`nx.v1.stylesheetProcessor`);
|
||||||
|
|
||||||
|
export const NX_STYLESHEET_PROCESSOR: FactoryProvider = {
|
||||||
|
provide: NX_STYLESHEET_PROCESSOR_TOKEN,
|
||||||
|
useFactory: () => StylesheetProcessor,
|
||||||
|
deps: [],
|
||||||
|
};
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Added watch mode parameter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as browserslist from 'browserslist';
|
||||||
|
import * as findCacheDirectory from 'find-cache-dir';
|
||||||
|
import { EsbuildExecutor } from 'ng-packagr/lib/esbuild/esbuild-executor';
|
||||||
|
import * as log from 'ng-packagr/lib/utils/log';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { MessageChannel, receiveMessageOnPort, Worker } from 'worker_threads';
|
||||||
|
|
||||||
|
export enum CssUrl {
|
||||||
|
inline = 'inline',
|
||||||
|
none = 'none',
|
||||||
|
}
|
||||||
|
export interface WorkerOptions {
|
||||||
|
filePath: string;
|
||||||
|
basePath: string;
|
||||||
|
browserslistData: string[];
|
||||||
|
cssUrl?: CssUrl;
|
||||||
|
styleIncludePaths?: string[];
|
||||||
|
cachePath: string;
|
||||||
|
alwaysUseWasm: boolean;
|
||||||
|
watch?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkerResult {
|
||||||
|
css: string;
|
||||||
|
warnings: string[];
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StylesheetProcessor {
|
||||||
|
private browserslistData: string[] | undefined;
|
||||||
|
private worker: Worker | undefined;
|
||||||
|
private readonly cachePath: string;
|
||||||
|
private alwaysUseWasm = !EsbuildExecutor.hasNativeSupport();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly basePath: string,
|
||||||
|
private readonly cssUrl?: CssUrl,
|
||||||
|
private readonly styleIncludePaths?: string[],
|
||||||
|
private readonly watch?: boolean
|
||||||
|
) {
|
||||||
|
this.cachePath =
|
||||||
|
findCacheDirectory({ name: 'ng-packagr-styles' }) || tmpdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
process(filePath: string) {
|
||||||
|
if (!this.worker) {
|
||||||
|
this.worker = new Worker(
|
||||||
|
join(__dirname, './stylesheet-processor-worker.js')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.browserslistData) {
|
||||||
|
this.browserslistData = browserslist(undefined, { path: this.basePath });
|
||||||
|
}
|
||||||
|
|
||||||
|
const workerOptions: WorkerOptions = {
|
||||||
|
filePath,
|
||||||
|
basePath: this.basePath,
|
||||||
|
cssUrl: this.cssUrl,
|
||||||
|
styleIncludePaths: this.styleIncludePaths,
|
||||||
|
browserslistData: this.browserslistData,
|
||||||
|
cachePath: this.cachePath,
|
||||||
|
alwaysUseWasm: this.alwaysUseWasm,
|
||||||
|
watch: this.watch,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ioChannel = new MessageChannel();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const signal = new Int32Array(new SharedArrayBuffer(4));
|
||||||
|
this.worker.postMessage(
|
||||||
|
{ signal, port: ioChannel.port1, workerOptions },
|
||||||
|
[ioChannel.port1]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sleep until signal[0] is 0
|
||||||
|
Atomics.wait(signal, 0, 0);
|
||||||
|
|
||||||
|
const { css, warnings, error } = receiveMessageOnPort(
|
||||||
|
ioChannel.port2
|
||||||
|
).message;
|
||||||
|
if (error) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings.forEach((msg) => log.warn(msg));
|
||||||
|
return css;
|
||||||
|
} finally {
|
||||||
|
ioChannel.port1.close();
|
||||||
|
ioChannel.port2.close();
|
||||||
|
this.worker.unref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { logger } from '@nrwl/devkit';
|
||||||
|
import { appRootPath } from '@nrwl/tao/src/utils/app-root';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { join, relative } from 'path';
|
||||||
|
import * as postcssImport from 'postcss-import';
|
||||||
|
|
||||||
|
export function getTailwindPostCssPluginsIfPresent(
|
||||||
|
basePath: string,
|
||||||
|
includePaths?: string[],
|
||||||
|
watch?: boolean
|
||||||
|
) {
|
||||||
|
// Try to find TailwindCSS configuration file in the project or workspace root.
|
||||||
|
const tailwindConfigFile = 'tailwind.config.js';
|
||||||
|
let tailwindConfigPath: string | undefined;
|
||||||
|
for (const path of [basePath, appRootPath]) {
|
||||||
|
const fullPath = join(path, tailwindConfigFile);
|
||||||
|
if (existsSync(fullPath)) {
|
||||||
|
tailwindConfigPath = fullPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only load Tailwind CSS plugin if configuration file was found.
|
||||||
|
if (!tailwindConfigPath) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let tailwindPackagePath: string | undefined;
|
||||||
|
try {
|
||||||
|
tailwindPackagePath = require.resolve('tailwindcss');
|
||||||
|
} catch {
|
||||||
|
const relativeTailwindConfigPath = relative(
|
||||||
|
appRootPath,
|
||||||
|
tailwindConfigPath
|
||||||
|
);
|
||||||
|
logger.warn(
|
||||||
|
`Tailwind CSS configuration file found (${relativeTailwindConfigPath})` +
|
||||||
|
` but the 'tailwindcss' package is not installed.` +
|
||||||
|
` To enable Tailwind CSS, please install the 'tailwindcss' package.`
|
||||||
|
);
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tailwindPackagePath) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env['TAILWIND_MODE'] === undefined) {
|
||||||
|
process.env['TAILWIND_MODE'] = watch ? 'watch' : 'build';
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
postcssImport({
|
||||||
|
addModulesDirectories: includePaths ?? [],
|
||||||
|
resolve: (url: string) => (url.startsWith('~') ? url.substr(1) : url),
|
||||||
|
}),
|
||||||
|
require(tailwindPackagePath)({ config: tailwindConfigPath }),
|
||||||
|
];
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ describe('Package executor', () => {
|
|||||||
let ngPackagrBuildMock: jest.Mock;
|
let ngPackagrBuildMock: jest.Mock;
|
||||||
let ngPackagerWatchSubject: BehaviorSubject<void>;
|
let ngPackagerWatchSubject: BehaviorSubject<void>;
|
||||||
let ngPackagrWatchMock: jest.Mock;
|
let ngPackagrWatchMock: jest.Mock;
|
||||||
|
let ngPackagrWithBuildTransformMock: jest.Mock;
|
||||||
let ngPackagrWithTsConfigMock: jest.Mock;
|
let ngPackagrWithTsConfigMock: jest.Mock;
|
||||||
let options: BuildAngularLibraryExecutorOptions;
|
let options: BuildAngularLibraryExecutorOptions;
|
||||||
let tsConfig: { options: { paths: { [key: string]: string[] } } };
|
let tsConfig: { options: { paths: { [key: string]: string[] } } };
|
||||||
@ -36,11 +37,13 @@ describe('Package executor', () => {
|
|||||||
ngPackagrBuildMock = jest.fn(() => Promise.resolve());
|
ngPackagrBuildMock = jest.fn(() => Promise.resolve());
|
||||||
ngPackagerWatchSubject = new BehaviorSubject<void>(undefined);
|
ngPackagerWatchSubject = new BehaviorSubject<void>(undefined);
|
||||||
ngPackagrWatchMock = jest.fn(() => ngPackagerWatchSubject.asObservable());
|
ngPackagrWatchMock = jest.fn(() => ngPackagerWatchSubject.asObservable());
|
||||||
|
ngPackagrWithBuildTransformMock = jest.fn();
|
||||||
ngPackagrWithTsConfigMock = jest.fn();
|
ngPackagrWithTsConfigMock = jest.fn();
|
||||||
(ngPackagr.ngPackagr as jest.Mock).mockImplementation(() => ({
|
(ngPackagr.NgPackagr as jest.Mock).mockImplementation(() => ({
|
||||||
build: ngPackagrBuildMock,
|
build: ngPackagrBuildMock,
|
||||||
forProject: jest.fn(),
|
forProject: jest.fn(),
|
||||||
watch: ngPackagrWatchMock,
|
watch: ngPackagrWatchMock,
|
||||||
|
withBuildTransform: ngPackagrWithBuildTransformMock,
|
||||||
withTsConfig: ngPackagrWithTsConfigMock,
|
withTsConfig: ngPackagrWithTsConfigMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,11 @@ import { resolve } from 'path';
|
|||||||
import { from } from 'rxjs';
|
import { from } from 'rxjs';
|
||||||
import { eachValueFrom } from 'rxjs-for-await';
|
import { eachValueFrom } from 'rxjs-for-await';
|
||||||
import { mapTo, switchMap, tap } from 'rxjs/operators';
|
import { mapTo, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { NX_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
||||||
|
import {
|
||||||
|
NX_PACKAGE_PROVIDERS,
|
||||||
|
NX_PACKAGE_TRANSFORM,
|
||||||
|
} from './ng-packagr-adjustments/ng-package/package.di';
|
||||||
import type { BuildAngularLibraryExecutorOptions } from './schema';
|
import type { BuildAngularLibraryExecutorOptions } from './schema';
|
||||||
|
|
||||||
async function initializeNgPackagr(
|
async function initializeNgPackagr(
|
||||||
@ -20,8 +25,13 @@ async function initializeNgPackagr(
|
|||||||
context: ExecutorContext,
|
context: ExecutorContext,
|
||||||
projectDependencies: DependentBuildableProjectNode[]
|
projectDependencies: DependentBuildableProjectNode[]
|
||||||
): Promise<NgPackagr> {
|
): Promise<NgPackagr> {
|
||||||
const packager = (await import('ng-packagr')).ngPackagr();
|
const packager = new (await import('ng-packagr')).NgPackagr([
|
||||||
|
...NX_PACKAGE_PROVIDERS,
|
||||||
|
...NX_ENTRY_POINT_PROVIDERS,
|
||||||
|
]);
|
||||||
|
|
||||||
packager.forProject(resolve(context.root, options.project));
|
packager.forProject(resolve(context.root, options.project));
|
||||||
|
packager.withBuildTransform(NX_PACKAGE_TRANSFORM.provide);
|
||||||
|
|
||||||
if (options.tsConfig) {
|
if (options.tsConfig) {
|
||||||
// read the tsconfig and modify its path in memory to
|
// read the tsconfig and modify its path in memory to
|
||||||
|
|||||||
@ -69,6 +69,7 @@ describe('lib', () => {
|
|||||||
// ASSERT
|
// ASSERT
|
||||||
const packageJson = readJson(appTree, '/package.json');
|
const packageJson = readJson(appTree, '/package.json');
|
||||||
expect(packageJson.devDependencies['ng-packagr']).toBeUndefined();
|
expect(packageJson.devDependencies['ng-packagr']).toBeUndefined();
|
||||||
|
expect(packageJson.devDependencies['postcss-import']).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update package.json when publishable', async () => {
|
it('should update package.json when publishable', async () => {
|
||||||
@ -81,6 +82,7 @@ describe('lib', () => {
|
|||||||
// ASSERT
|
// ASSERT
|
||||||
const packageJson = readJson(appTree, '/package.json');
|
const packageJson = readJson(appTree, '/package.json');
|
||||||
expect(packageJson.devDependencies['ng-packagr']).toBeDefined();
|
expect(packageJson.devDependencies['ng-packagr']).toBeDefined();
|
||||||
|
expect(packageJson.devDependencies['postcss-import']).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update tsconfig.lib.prod.json when enableIvy', async () => {
|
it('should update tsconfig.lib.prod.json when enableIvy', async () => {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
getWorkspaceLayout,
|
|
||||||
installPackagesTask,
|
installPackagesTask,
|
||||||
moveFilesToNewDirectory,
|
moveFilesToNewDirectory,
|
||||||
Tree,
|
Tree,
|
||||||
@ -70,6 +70,10 @@ export async function libraryGenerator(host: Tree, schema: Partial<Schema>) {
|
|||||||
setStrictMode(host, options);
|
setStrictMode(host, options);
|
||||||
await addLinting(host, options);
|
await addLinting(host, options);
|
||||||
|
|
||||||
|
if (options.publishable) {
|
||||||
|
addDependenciesToPackageJson(host, {}, { 'postcss-import': '^14.0.2' });
|
||||||
|
}
|
||||||
|
|
||||||
if (options.standaloneConfig) {
|
if (options.standaloneConfig) {
|
||||||
await convertToNxProjectGenerator(host, {
|
await convertToNxProjectGenerator(host, {
|
||||||
project: options.name,
|
project: options.name,
|
||||||
|
|||||||
@ -0,0 +1,30 @@
|
|||||||
|
import { readJson, Tree, updateJson } from '@nrwl/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import addPostCssImport from './add-postcss-import';
|
||||||
|
|
||||||
|
describe('add-postcss-import migration', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add postcss-import when ng-packagr is not installed', () => {
|
||||||
|
addPostCssImport(tree);
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
expect(packageJson.devDependencies['postcss-import']).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add postcss-import when ng-packagr is installed', () => {
|
||||||
|
updateJson(tree, 'package.json', (json) => {
|
||||||
|
json.devDependencies['ng-packagr'] = '~12.2.3';
|
||||||
|
return json;
|
||||||
|
});
|
||||||
|
|
||||||
|
addPostCssImport(tree);
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
expect(packageJson.devDependencies['postcss-import']).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
formatFiles,
|
||||||
|
readJson,
|
||||||
|
Tree,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
|
||||||
|
export default async function (tree: Tree) {
|
||||||
|
const { devDependencies } = readJson(tree, 'package.json');
|
||||||
|
|
||||||
|
// Don't add if ng-packagr is not installed
|
||||||
|
if (!devDependencies['ng-packagr']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const task = addDependenciesToPackageJson(
|
||||||
|
tree,
|
||||||
|
{},
|
||||||
|
{ 'postcss-import': '^14.0.2' }
|
||||||
|
);
|
||||||
|
|
||||||
|
await formatFiles(tree);
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
@ -15,10 +15,26 @@ const IGNORE_MATCHES = {
|
|||||||
'@ngrx/router-store',
|
'@ngrx/router-store',
|
||||||
'@ngrx/store',
|
'@ngrx/store',
|
||||||
'@storybook/angular',
|
'@storybook/angular',
|
||||||
'injection-js',
|
|
||||||
'ng-packagr',
|
|
||||||
'rxjs',
|
'rxjs',
|
||||||
'semver',
|
'semver',
|
||||||
|
// installed dynamically by the library generator
|
||||||
|
'ng-packagr',
|
||||||
|
// ng-packagr deps, some are handled if not installed
|
||||||
|
'injection-js',
|
||||||
|
'browserslist',
|
||||||
|
'cacache',
|
||||||
|
'find-cache-dir',
|
||||||
|
'less',
|
||||||
|
'node-sass',
|
||||||
|
'node-sass-tilde-importer',
|
||||||
|
'ora',
|
||||||
|
'postcss',
|
||||||
|
'postcss-import',
|
||||||
|
'postcss-preset-env',
|
||||||
|
'postcss-url',
|
||||||
|
'sass',
|
||||||
|
'stylus',
|
||||||
|
'tailwindcss',
|
||||||
],
|
],
|
||||||
cli: ['@nrwl/cli'],
|
cli: ['@nrwl/cli'],
|
||||||
cypress: ['cypress', '@angular-devkit/schematics', '@nrwl/cypress'],
|
cypress: ['cypress', '@angular-devkit/schematics', '@nrwl/cypress'],
|
||||||
|
|||||||
17
yarn.lock
17
yarn.lock
@ -11813,6 +11813,11 @@ es6-weak-map@^2.0.3:
|
|||||||
es6-iterator "^2.0.3"
|
es6-iterator "^2.0.3"
|
||||||
es6-symbol "^3.1.1"
|
es6-symbol "^3.1.1"
|
||||||
|
|
||||||
|
esbuild-wasm@^0.12.15:
|
||||||
|
version "0.12.29"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild-wasm/-/esbuild-wasm-0.12.29.tgz#1d210bb7d463b2ca51c54d69bb4192d9709f6100"
|
||||||
|
integrity sha512-amSuB/qOGnTFYLOxGHDGosQbOKZnrinniPHFf6ZxzeNH7WAjLkjXluKyKAtX2YuhTkUXm9XV9igl13iqYZ44fQ==
|
||||||
|
|
||||||
esbuild@0.12.17:
|
esbuild@0.12.17:
|
||||||
version "0.12.17"
|
version "0.12.17"
|
||||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.17.tgz#5816f905c2905de0ebbc658860df7b5b48afbcd3"
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.17.tgz#5816f905c2905de0ebbc658860df7b5b48afbcd3"
|
||||||
@ -18358,10 +18363,10 @@ next@^11.1.2:
|
|||||||
"@next/swc-linux-x64-gnu" "11.1.2"
|
"@next/swc-linux-x64-gnu" "11.1.2"
|
||||||
"@next/swc-win32-x64-msvc" "11.1.2"
|
"@next/swc-win32-x64-msvc" "11.1.2"
|
||||||
|
|
||||||
ng-packagr@~12.2.0:
|
ng-packagr@~12.2.3:
|
||||||
version "12.2.0"
|
version "12.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-12.2.0.tgz#53fe47391b5ddaf5f2c24eaecb23d8a10235d887"
|
resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-12.2.3.tgz#1597f8e70a78ab88bb395675358942bfc14cbc07"
|
||||||
integrity sha512-M/qq78Gb4q13t6SFX70W2DrPxyooSkLwXzhWozjD8yWGihx4q+54a72ODGx7jIrB4fQgrGDcMUTM7t1zGYir8Q==
|
integrity sha512-2KHoglc7UgJMnzkytzZ1wU+IEkb6UrxoU4QZxnF5BSFh9vjUra2nCXH+EKkvxD3WTj0ikXmNIMeNWbwoZpGkgA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@rollup/plugin-commonjs" "^20.0.0"
|
"@rollup/plugin-commonjs" "^20.0.0"
|
||||||
"@rollup/plugin-json" "^4.1.0"
|
"@rollup/plugin-json" "^4.1.0"
|
||||||
@ -18373,7 +18378,7 @@ ng-packagr@~12.2.0:
|
|||||||
chokidar "^3.5.1"
|
chokidar "^3.5.1"
|
||||||
commander "^8.0.0"
|
commander "^8.0.0"
|
||||||
dependency-graph "^0.11.0"
|
dependency-graph "^0.11.0"
|
||||||
esbuild "^0.12.15"
|
esbuild-wasm "^0.12.15"
|
||||||
find-cache-dir "^3.3.1"
|
find-cache-dir "^3.3.1"
|
||||||
glob "^7.1.6"
|
glob "^7.1.6"
|
||||||
injection-js "^2.4.0"
|
injection-js "^2.4.0"
|
||||||
@ -18389,6 +18394,8 @@ ng-packagr@~12.2.0:
|
|||||||
rxjs "^6.5.0"
|
rxjs "^6.5.0"
|
||||||
sass "^1.32.8"
|
sass "^1.32.8"
|
||||||
stylus "^0.54.8"
|
stylus "^0.54.8"
|
||||||
|
optionalDependencies:
|
||||||
|
esbuild "^0.12.15"
|
||||||
|
|
||||||
ngrx-store-freeze@0.2.4:
|
ngrx-store-freeze@0.2.4:
|
||||||
version "0.2.4"
|
version "0.2.4"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user