feat(angular): add tailwind support for buildable libraries (#7961)
This commit is contained in:
parent
56aaeb7931
commit
9a08a83591
@ -22,6 +22,12 @@ Possible values: `dependencies`, `peerDependencies`
|
|||||||
|
|
||||||
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
||||||
|
|
||||||
|
### tailwindConfig
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured.
|
||||||
|
|
||||||
### tsConfig
|
### tsConfig
|
||||||
|
|
||||||
Type: `string`
|
Type: `string`
|
||||||
|
|||||||
@ -22,6 +22,12 @@ Possible values: `dependencies`, `peerDependencies`
|
|||||||
|
|
||||||
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
||||||
|
|
||||||
|
### tailwindConfig
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured.
|
||||||
|
|
||||||
### tsConfig
|
### tsConfig
|
||||||
|
|
||||||
Type: `string`
|
Type: `string`
|
||||||
|
|||||||
@ -22,6 +22,12 @@ Possible values: `dependencies`, `peerDependencies`
|
|||||||
|
|
||||||
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
||||||
|
|
||||||
|
### tailwindConfig
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured.
|
||||||
|
|
||||||
### tsConfig
|
### tsConfig
|
||||||
|
|
||||||
Type: `string`
|
Type: `string`
|
||||||
|
|||||||
@ -22,6 +22,12 @@ Possible values: `dependencies`, `peerDependencies`
|
|||||||
|
|
||||||
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
||||||
|
|
||||||
|
### tailwindConfig
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured.
|
||||||
|
|
||||||
### tsConfig
|
### tsConfig
|
||||||
|
|
||||||
Type: `string`
|
Type: `string`
|
||||||
|
|||||||
@ -22,6 +22,12 @@ Possible values: `dependencies`, `peerDependencies`
|
|||||||
|
|
||||||
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
||||||
|
|
||||||
|
### tailwindConfig
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured.
|
||||||
|
|
||||||
### tsConfig
|
### tsConfig
|
||||||
|
|
||||||
Type: `string`
|
Type: `string`
|
||||||
|
|||||||
@ -22,6 +22,12 @@ Possible values: `dependencies`, `peerDependencies`
|
|||||||
|
|
||||||
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.
|
||||||
|
|
||||||
|
### tailwindConfig
|
||||||
|
|
||||||
|
Type: `string`
|
||||||
|
|
||||||
|
The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured.
|
||||||
|
|
||||||
### tsConfig
|
### tsConfig
|
||||||
|
|
||||||
Type: `string`
|
Type: `string`
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import {
|
|||||||
checkFilesExist,
|
checkFilesExist,
|
||||||
getSelectedPackageManager,
|
getSelectedPackageManager,
|
||||||
newProject,
|
newProject,
|
||||||
|
packageInstall,
|
||||||
|
readFile,
|
||||||
readJson,
|
readJson,
|
||||||
removeProject,
|
removeProject,
|
||||||
runCLI,
|
runCLI,
|
||||||
@ -11,6 +13,7 @@ import {
|
|||||||
updateFile,
|
updateFile,
|
||||||
} from '@nrwl/e2e/utils';
|
} from '@nrwl/e2e/utils';
|
||||||
import { names } from '@nrwl/devkit';
|
import { names } from '@nrwl/devkit';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
describe('Angular Package', () => {
|
describe('Angular Package', () => {
|
||||||
['publishable', 'buildable'].forEach((testConfig) => {
|
['publishable', 'buildable'].forEach((testConfig) => {
|
||||||
@ -22,7 +25,6 @@ describe('Angular Package', () => {
|
|||||||
* /
|
* /
|
||||||
* parentLib =>
|
* parentLib =>
|
||||||
* \
|
* \
|
||||||
* \
|
|
||||||
* childLib2
|
* childLib2
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@ -141,13 +143,7 @@ describe('Angular Package', () => {
|
|||||||
|
|
||||||
afterEach(() => removeProject({ onlyOnCI: true }));
|
afterEach(() => removeProject({ onlyOnCI: true }));
|
||||||
|
|
||||||
it('should build the library when it does not have any deps', () => {
|
it('should build properly and update the parent package.json with the dependencies', () => {
|
||||||
runCLI(`build ${childLib}`);
|
|
||||||
|
|
||||||
checkFilesExist(`dist/libs/${childLib}/package.json`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should properly add references to any dependency into the parent package.json', () => {
|
|
||||||
runCLI(`build ${childLib}`);
|
runCLI(`build ${childLib}`);
|
||||||
runCLI(`build ${childLib2}`);
|
runCLI(`build ${childLib2}`);
|
||||||
runCLI(`build ${parentLib}`);
|
runCLI(`build ${parentLib}`);
|
||||||
@ -173,56 +169,281 @@ describe('Angular Package', () => {
|
|||||||
|
|
||||||
describe('Publishable library secondary entry point', () => {
|
describe('Publishable library secondary entry point', () => {
|
||||||
let project: string;
|
let project: string;
|
||||||
let parentLib: string;
|
let lib: string;
|
||||||
let childLib: string;
|
|
||||||
let entryPoint: string;
|
let entryPoint: string;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
project = newProject();
|
project = newProject();
|
||||||
parentLib = uniq('parentlib');
|
lib = uniq('lib');
|
||||||
childLib = uniq('childlib');
|
|
||||||
entryPoint = uniq('entrypoint');
|
entryPoint = uniq('entrypoint');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nrwl/angular:lib ${parentLib} --publishable --importPath=@${project}/${parentLib} --no-interactive`
|
`generate @nrwl/angular:lib ${lib} --publishable --importPath=@${project}/${lib} --no-interactive`
|
||||||
);
|
);
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nrwl/angular:lib ${childLib} --publishable --importPath=@${project}/${childLib} --no-interactive`
|
`generate @nrwl/angular:secondary-entry-point --name=${entryPoint} --library=${lib} --no-interactive`
|
||||||
);
|
|
||||||
runCLI(
|
|
||||||
`generate @nrwl/angular:secondary-entry-point --name=${entryPoint} --library=${childLib} --no-interactive`
|
|
||||||
);
|
|
||||||
|
|
||||||
updateFile(
|
|
||||||
`libs/${parentLib}/src/lib/${parentLib}.module.ts`,
|
|
||||||
`
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { ${
|
|
||||||
names(entryPoint).className
|
|
||||||
}Module } from '@${project}/${childLib}/${entryPoint}';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [CommonModule, ${names(entryPoint).className}Module],
|
|
||||||
})
|
|
||||||
export class Lib1Module {}
|
|
||||||
`
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should build successfully', () => {
|
it('should build successfully', () => {
|
||||||
const buildOutput = runCLI(`build ${parentLib}`);
|
const buildOutput = runCLI(`build ${lib}`);
|
||||||
|
|
||||||
expect(buildOutput).toContain(
|
expect(buildOutput).toContain(
|
||||||
`Building entry point '@${project}/${childLib}'`
|
`Building entry point '@${project}/${lib}'`
|
||||||
);
|
);
|
||||||
expect(buildOutput).toContain(
|
expect(buildOutput).toContain(
|
||||||
`Building entry point '@${project}/${childLib}/${entryPoint}'`
|
`Building entry point '@${project}/${lib}/${entryPoint}'`
|
||||||
);
|
|
||||||
expect(buildOutput).toContain(
|
|
||||||
`Building entry point '@${project}/${parentLib}'`
|
|
||||||
);
|
);
|
||||||
expect(buildOutput).toContain('Running target "build" succeeded');
|
expect(buildOutput).toContain('Running target "build" succeeded');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Tailwind support', () => {
|
||||||
|
let projectScope: string;
|
||||||
|
let buildLibExecutorOption: string;
|
||||||
|
let buildLibProjectConfig: string;
|
||||||
|
let buildLibRootConfig: string;
|
||||||
|
let pubLibExecutorOption: string;
|
||||||
|
let pubLibProjectConfig: string;
|
||||||
|
let pubLibRootConfig: string;
|
||||||
|
|
||||||
|
const customTailwindConfigFile = 'custom-tailwind.config.js';
|
||||||
|
|
||||||
|
const spacing = {
|
||||||
|
rootConfig: {
|
||||||
|
sm: '2px',
|
||||||
|
md: '4px',
|
||||||
|
lg: '8px',
|
||||||
|
},
|
||||||
|
projectConfig: {
|
||||||
|
sm: '1px',
|
||||||
|
md: '2px',
|
||||||
|
lg: '4px',
|
||||||
|
},
|
||||||
|
executorOption: {
|
||||||
|
sm: '4px',
|
||||||
|
md: '8px',
|
||||||
|
lg: '16px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const createWorkspaceTailwindConfigFile = () => {
|
||||||
|
const tailwindConfigFile = 'tailwind.config.js';
|
||||||
|
|
||||||
|
const tailwindConfig = `module.exports = {
|
||||||
|
mode: 'jit',
|
||||||
|
purge: ['./apps/**/*.{html,ts}', './libs/**/*.{html,ts}'],
|
||||||
|
darkMode: false,
|
||||||
|
theme: {
|
||||||
|
spacing: {
|
||||||
|
sm: '${spacing.rootConfig.sm}',
|
||||||
|
md: '${spacing.rootConfig.md}',
|
||||||
|
lg: '${spacing.rootConfig.lg}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
variants: { extend: {} },
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
updateFile(tailwindConfigFile, tailwindConfig);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createTailwindConfigFile = (
|
||||||
|
dir: string,
|
||||||
|
lib: string,
|
||||||
|
libSpacing: typeof spacing['executorOption'],
|
||||||
|
tailwindConfigFile = 'tailwind.config.js'
|
||||||
|
) => {
|
||||||
|
const tailwindConfigFilePath = join(dir, tailwindConfigFile);
|
||||||
|
|
||||||
|
const tailwindConfig = `const { createGlobPatternsForDependencies } = require('@nrwl/angular/tailwind');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: 'jit',
|
||||||
|
purge: [
|
||||||
|
'./libs/${lib}/src/**/*.{html,ts}',
|
||||||
|
...createGlobPatternsForDependencies(__dirname),
|
||||||
|
],
|
||||||
|
darkMode: false,
|
||||||
|
theme: {
|
||||||
|
spacing: {
|
||||||
|
sm: '${libSpacing.sm}',
|
||||||
|
md: '${libSpacing.md}',
|
||||||
|
lg: '${libSpacing.lg}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
variants: { extend: {} },
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
|
`;
|
||||||
|
|
||||||
|
updateFile(tailwindConfigFilePath, tailwindConfig);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addTailwindConfigToProject = (lib: string) => {
|
||||||
|
const angularJson = readJson('angular.json');
|
||||||
|
angularJson.projects[
|
||||||
|
lib
|
||||||
|
].architect.build.options.tailwindConfig = `libs/${lib}/${customTailwindConfigFile}`;
|
||||||
|
updateFile('angular.json', JSON.stringify(angularJson, null, 2));
|
||||||
|
};
|
||||||
|
|
||||||
|
const createLibComponent = (lib: string) => {
|
||||||
|
updateFile(
|
||||||
|
`libs/${lib}/src/lib/foo.component.ts`,
|
||||||
|
`import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: '${projectScope}-foo',
|
||||||
|
template: '<button class="custom-btn">Click me!</button>',
|
||||||
|
styles: [\`
|
||||||
|
.custom-btn {
|
||||||
|
@apply m-md p-sm;
|
||||||
|
}
|
||||||
|
\`]
|
||||||
|
})
|
||||||
|
export class FooComponent {}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
updateFile(
|
||||||
|
`libs/${lib}/src/lib/${lib}.module.ts`,
|
||||||
|
`import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FooComponent } from './foo.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [FooComponent],
|
||||||
|
exports: [FooComponent],
|
||||||
|
})
|
||||||
|
export class LibModule {}
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
updateFile(
|
||||||
|
`libs/${lib}/src/index.ts`,
|
||||||
|
`export * from './lib/foo.component';
|
||||||
|
export * from './lib/${lib}.module';
|
||||||
|
`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const projectName = uniq('proj');
|
||||||
|
|
||||||
|
projectScope = newProject({ name: projectName });
|
||||||
|
buildLibExecutorOption = uniq('build-lib-executor-option');
|
||||||
|
buildLibProjectConfig = uniq('build-lib-project-config');
|
||||||
|
buildLibRootConfig = uniq('build-lib-root-config');
|
||||||
|
pubLibExecutorOption = uniq('pub-lib-executor-option');
|
||||||
|
pubLibProjectConfig = uniq('pub-lib-project-config');
|
||||||
|
pubLibRootConfig = uniq('pub-lib-root-config');
|
||||||
|
|
||||||
|
// Install Tailwind required packages.
|
||||||
|
// TODO: Remove this when Tailwind generator is created and used.
|
||||||
|
packageInstall('tailwindcss postcss autoprefixer', projectName, 'latest');
|
||||||
|
|
||||||
|
// Create Tailwind config in the workspace root.
|
||||||
|
createWorkspaceTailwindConfigFile();
|
||||||
|
|
||||||
|
// Setup buildable libs
|
||||||
|
|
||||||
|
// Buildable lib with tailwind config specified in the project config
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:lib ${buildLibExecutorOption} --buildable --no-interactive`
|
||||||
|
);
|
||||||
|
createLibComponent(buildLibExecutorOption);
|
||||||
|
createTailwindConfigFile(
|
||||||
|
`libs/${buildLibExecutorOption}`,
|
||||||
|
buildLibExecutorOption,
|
||||||
|
spacing.executorOption,
|
||||||
|
customTailwindConfigFile
|
||||||
|
);
|
||||||
|
addTailwindConfigToProject(buildLibExecutorOption);
|
||||||
|
|
||||||
|
// Buildable lib with tailwind config located in the project root
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:lib ${buildLibProjectConfig} --buildable --no-interactive`
|
||||||
|
);
|
||||||
|
createLibComponent(buildLibProjectConfig);
|
||||||
|
createTailwindConfigFile(
|
||||||
|
`libs/${buildLibProjectConfig}`,
|
||||||
|
buildLibProjectConfig,
|
||||||
|
spacing.projectConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
// Buildable lib with tailwind config located in the workspace root
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:lib ${buildLibRootConfig} --buildable --no-interactive`
|
||||||
|
);
|
||||||
|
createLibComponent(buildLibRootConfig);
|
||||||
|
|
||||||
|
// Publishable libs
|
||||||
|
|
||||||
|
// Publishable lib with tailwind config specified in the project config
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:lib ${pubLibExecutorOption} --publishable --importPath=@${projectScope}/${pubLibExecutorOption} --no-interactive`
|
||||||
|
);
|
||||||
|
createLibComponent(pubLibExecutorOption);
|
||||||
|
createTailwindConfigFile(
|
||||||
|
`libs/${pubLibExecutorOption}`,
|
||||||
|
pubLibExecutorOption,
|
||||||
|
spacing.executorOption,
|
||||||
|
customTailwindConfigFile
|
||||||
|
);
|
||||||
|
addTailwindConfigToProject(pubLibExecutorOption);
|
||||||
|
|
||||||
|
// Publishable lib with tailwind config located in the project root
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:lib ${pubLibProjectConfig} --publishable --importPath=@${projectScope}/${pubLibProjectConfig} --no-interactive`
|
||||||
|
);
|
||||||
|
createLibComponent(pubLibProjectConfig);
|
||||||
|
createTailwindConfigFile(
|
||||||
|
`libs/${pubLibProjectConfig}`,
|
||||||
|
pubLibProjectConfig,
|
||||||
|
spacing.projectConfig
|
||||||
|
);
|
||||||
|
|
||||||
|
// Publishable lib with tailwind config located in the workspace root
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/angular:lib ${pubLibRootConfig} --publishable --importPath=@${projectScope}/${pubLibRootConfig} --no-interactive`
|
||||||
|
);
|
||||||
|
createLibComponent(pubLibRootConfig);
|
||||||
|
});
|
||||||
|
|
||||||
|
const assertComponentStyles = (
|
||||||
|
lib: string,
|
||||||
|
libSpacing: typeof spacing['executorOption']
|
||||||
|
) => {
|
||||||
|
const builtComponentContent = readFile(
|
||||||
|
`dist/libs/${lib}/esm2020/lib/foo.component.mjs`
|
||||||
|
);
|
||||||
|
let expectedStylesRegex = new RegExp(
|
||||||
|
`styles: \\[\\"\\.custom\\-btn(\\[_ngcontent\\-%COMP%\\])?{margin:${libSpacing.md};padding:${libSpacing.sm}}(\\\\n)?\\"\\]`
|
||||||
|
);
|
||||||
|
expect(builtComponentContent).toMatch(expectedStylesRegex);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should build and output the right styles based on the tailwind config', () => {
|
||||||
|
runCLI(`build ${buildLibExecutorOption}`);
|
||||||
|
assertComponentStyles(buildLibExecutorOption, spacing.executorOption);
|
||||||
|
|
||||||
|
runCLI(`build ${buildLibProjectConfig}`);
|
||||||
|
assertComponentStyles(buildLibProjectConfig, spacing.projectConfig);
|
||||||
|
|
||||||
|
runCLI(`build ${buildLibRootConfig}`);
|
||||||
|
assertComponentStyles(buildLibRootConfig, spacing.rootConfig);
|
||||||
|
|
||||||
|
runCLI(`build ${pubLibExecutorOption}`);
|
||||||
|
assertComponentStyles(pubLibExecutorOption, spacing.executorOption);
|
||||||
|
|
||||||
|
runCLI(`build ${pubLibProjectConfig}`);
|
||||||
|
assertComponentStyles(pubLibProjectConfig, spacing.projectConfig);
|
||||||
|
|
||||||
|
runCLI(`build ${pubLibRootConfig}`);
|
||||||
|
assertComponentStyles(pubLibRootConfig, spacing.rootConfig);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,11 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN.
|
||||||
|
*/
|
||||||
|
|
||||||
import { InjectionToken, Provider } from 'injection-js';
|
import { InjectionToken, Provider } from 'injection-js';
|
||||||
import type { Transform } from 'ng-packagr/lib/graph/transform';
|
import type { Transform } from 'ng-packagr/lib/graph/transform';
|
||||||
import { provideTransform } from 'ng-packagr/lib/graph/transform.di';
|
import { provideTransform } from 'ng-packagr/lib/graph/transform.di';
|
||||||
import { OPTIONS_TOKEN } from 'ng-packagr/lib/ng-package/options.di';
|
|
||||||
import {
|
import {
|
||||||
STYLESHEET_PROCESSOR,
|
NX_STYLESHEET_PROCESSOR,
|
||||||
STYLESHEET_PROCESSOR_TOKEN,
|
NX_STYLESHEET_PROCESSOR_TOKEN,
|
||||||
} from 'ng-packagr/lib/styles/stylesheet-processor.di';
|
} from '../../styles/stylesheet-processor.di';
|
||||||
|
import { NX_OPTIONS_TOKEN } from '../options.di';
|
||||||
import { nxCompileNgcTransformFactory } from './compile-ngc.transform';
|
import { nxCompileNgcTransformFactory } from './compile-ngc.transform';
|
||||||
|
|
||||||
export const NX_COMPILE_NGC_TOKEN = new InjectionToken<Transform>(
|
export const NX_COMPILE_NGC_TOKEN = new InjectionToken<Transform>(
|
||||||
@ -14,9 +24,9 @@ export const NX_COMPILE_NGC_TOKEN = new InjectionToken<Transform>(
|
|||||||
export const NX_COMPILE_NGC_TRANSFORM = provideTransform({
|
export const NX_COMPILE_NGC_TRANSFORM = provideTransform({
|
||||||
provide: NX_COMPILE_NGC_TOKEN,
|
provide: NX_COMPILE_NGC_TOKEN,
|
||||||
useFactory: nxCompileNgcTransformFactory,
|
useFactory: nxCompileNgcTransformFactory,
|
||||||
deps: [STYLESHEET_PROCESSOR_TOKEN, OPTIONS_TOKEN],
|
deps: [NX_STYLESHEET_PROCESSOR_TOKEN, NX_OPTIONS_TOKEN],
|
||||||
});
|
});
|
||||||
export const NX_COMPILE_NGC_PROVIDERS: Provider[] = [
|
export const NX_COMPILE_NGC_PROVIDERS: Provider[] = [
|
||||||
STYLESHEET_PROCESSOR,
|
NX_STYLESHEET_PROCESSOR,
|
||||||
NX_COMPILE_NGC_TRANSFORM,
|
NX_COMPILE_NGC_TRANSFORM,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* Adapted from the original ngPackagr source.
|
* Adapted from the original ngPackagr source.
|
||||||
*
|
*
|
||||||
* Excludes the ngcc compilation which is not needed
|
* Changes made:
|
||||||
* since these libraries will be compiled by the ngtsc.
|
* - Use our own StylesheetProcessor files instead of the ones provide by ng-packagr.
|
||||||
|
* - Excludes the ngcc compilation for faster builds.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Transform } from 'ng-packagr/lib/graph/transform';
|
import type { Transform } from 'ng-packagr/lib/graph/transform';
|
||||||
@ -11,12 +12,12 @@ import {
|
|||||||
isEntryPoint,
|
isEntryPoint,
|
||||||
isEntryPointInProgress,
|
isEntryPointInProgress,
|
||||||
} from 'ng-packagr/lib/ng-package/nodes';
|
} from 'ng-packagr/lib/ng-package/nodes';
|
||||||
import { NgPackagrOptions } from 'ng-packagr/lib/ng-package/options.di';
|
|
||||||
import type { StylesheetProcessor as StylesheetProcessorClass } from 'ng-packagr/lib/styles/stylesheet-processor';
|
|
||||||
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import { compileSourceFiles } from '../../ngc/compile-source-files';
|
import { compileSourceFiles } from '../../ngc/compile-source-files';
|
||||||
|
import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor';
|
||||||
|
import { NgPackagrOptions } from '../options.di';
|
||||||
|
|
||||||
export const nxCompileNgcTransformFactory = (
|
export const nxCompileNgcTransformFactory = (
|
||||||
StylesheetProcessor: typeof StylesheetProcessorClass,
|
StylesheetProcessor: typeof StylesheetProcessorClass,
|
||||||
@ -42,8 +43,10 @@ export const nxCompileNgcTransformFactory = (
|
|||||||
basePath,
|
basePath,
|
||||||
cssUrl,
|
cssUrl,
|
||||||
styleIncludePaths,
|
styleIncludePaths,
|
||||||
options.cacheEnabled && options.cacheDirectory
|
options.cacheEnabled && options.cacheDirectory,
|
||||||
);
|
options.watch,
|
||||||
|
options.tailwindConfig
|
||||||
|
) as any;
|
||||||
|
|
||||||
await compileSourceFiles(
|
await compileSourceFiles(
|
||||||
graph,
|
graph,
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Changes made:
|
* Changes made:
|
||||||
* - Provide our own writePackageTransform function.
|
* - Provide our own writePackageTransform function.
|
||||||
|
* - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { InjectionToken } from 'injection-js';
|
import { InjectionToken } from 'injection-js';
|
||||||
@ -11,7 +12,7 @@ import {
|
|||||||
provideTransform,
|
provideTransform,
|
||||||
TransformProvider,
|
TransformProvider,
|
||||||
} from 'ng-packagr/lib/graph/transform.di';
|
} from 'ng-packagr/lib/graph/transform.di';
|
||||||
import { OPTIONS_TOKEN } from 'ng-packagr/lib/ng-package/options.di';
|
import { NX_OPTIONS_TOKEN } from '../options.di';
|
||||||
import { nxWritePackageTransform } from './write-package.transform';
|
import { nxWritePackageTransform } from './write-package.transform';
|
||||||
|
|
||||||
export const NX_WRITE_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
export const NX_WRITE_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
||||||
@ -20,5 +21,5 @@ export const NX_WRITE_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
|||||||
export const NX_WRITE_PACKAGE_TRANSFORM: TransformProvider = provideTransform({
|
export const NX_WRITE_PACKAGE_TRANSFORM: TransformProvider = provideTransform({
|
||||||
provide: NX_WRITE_PACKAGE_TRANSFORM_TOKEN,
|
provide: NX_WRITE_PACKAGE_TRANSFORM_TOKEN,
|
||||||
useFactory: nxWritePackageTransform,
|
useFactory: nxWritePackageTransform,
|
||||||
deps: [OPTIONS_TOKEN],
|
deps: [NX_OPTIONS_TOKEN],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Use our own options interface to add support for tailwindConfig.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as findCacheDirectory from 'find-cache-dir';
|
||||||
|
import { InjectionToken, Provider, ValueProvider } from 'injection-js';
|
||||||
|
import { NgPackagrOptions as NgPackagrOptionsBase } from 'ng-packagr/lib/ng-package/options.di';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
export interface NgPackagrOptions extends NgPackagrOptionsBase {
|
||||||
|
tailwindConfig?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NX_OPTIONS_TOKEN = new InjectionToken<NgPackagrOptions>(
|
||||||
|
`nx.v1.options`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const nxProvideOptions = (
|
||||||
|
options: NgPackagrOptions = {}
|
||||||
|
): ValueProvider => ({
|
||||||
|
provide: NX_OPTIONS_TOKEN,
|
||||||
|
useValue: normalizeOptions(options),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NX_DEFAULT_OPTIONS_PROVIDER: Provider = nxProvideOptions();
|
||||||
|
|
||||||
|
function normalizeOptions(options: NgPackagrOptions = {}) {
|
||||||
|
const ciEnv = process.env['CI'];
|
||||||
|
const isCI = ciEnv?.toLowerCase() === 'true' || ciEnv === '1';
|
||||||
|
const { cacheEnabled = !isCI, cacheDirectory = findCachePath() } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
cacheEnabled,
|
||||||
|
cacheDirectory,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function findCachePath(): string {
|
||||||
|
const name = 'ng-packagr';
|
||||||
|
|
||||||
|
return findCacheDirectory({ name }) || resolve(tmpdir(), name);
|
||||||
|
}
|
||||||
@ -3,6 +3,8 @@
|
|||||||
*
|
*
|
||||||
* Changes made:
|
* Changes made:
|
||||||
* - Use NX_ENTRY_POINT_TRANSFORM_TOKEN instead of ENTRY_POINT_TRANSFORM_TOKEN.
|
* - Use NX_ENTRY_POINT_TRANSFORM_TOKEN instead of ENTRY_POINT_TRANSFORM_TOKEN.
|
||||||
|
* - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN.
|
||||||
|
* - USE NX_DEFAULT_OPTIONS_PROVIDER instead of DEFAULT_OPTIONS_PROVIDER.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Provider } from 'injection-js';
|
import type { Provider } from 'injection-js';
|
||||||
@ -17,13 +19,10 @@ import {
|
|||||||
INIT_TS_CONFIG_TOKEN,
|
INIT_TS_CONFIG_TOKEN,
|
||||||
INIT_TS_CONFIG_TRANSFORM,
|
INIT_TS_CONFIG_TRANSFORM,
|
||||||
} from 'ng-packagr/lib/ng-package/entry-point/init-tsconfig.di';
|
} 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 { packageTransformFactory } from 'ng-packagr/lib/ng-package/package.transform';
|
||||||
import { PROJECT_TOKEN } from 'ng-packagr/lib/project.di';
|
import { PROJECT_TOKEN } from 'ng-packagr/lib/project.di';
|
||||||
import { NX_ENTRY_POINT_TRANSFORM_TOKEN } from './entry-point/entry-point.di';
|
import { NX_ENTRY_POINT_TRANSFORM_TOKEN } from './entry-point/entry-point.di';
|
||||||
|
import { NX_DEFAULT_OPTIONS_PROVIDER, NX_OPTIONS_TOKEN } from './options.di';
|
||||||
|
|
||||||
export const NX_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
export const NX_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
||||||
`nx.v1.packageTransform`
|
`nx.v1.packageTransform`
|
||||||
@ -34,7 +33,7 @@ export const NX_PACKAGE_TRANSFORM = provideTransform({
|
|||||||
useFactory: packageTransformFactory,
|
useFactory: packageTransformFactory,
|
||||||
deps: [
|
deps: [
|
||||||
PROJECT_TOKEN,
|
PROJECT_TOKEN,
|
||||||
OPTIONS_TOKEN,
|
NX_OPTIONS_TOKEN,
|
||||||
INIT_TS_CONFIG_TOKEN,
|
INIT_TS_CONFIG_TOKEN,
|
||||||
ANALYSE_SOURCES_TOKEN,
|
ANALYSE_SOURCES_TOKEN,
|
||||||
NX_ENTRY_POINT_TRANSFORM_TOKEN,
|
NX_ENTRY_POINT_TRANSFORM_TOKEN,
|
||||||
@ -43,7 +42,7 @@ export const NX_PACKAGE_TRANSFORM = provideTransform({
|
|||||||
|
|
||||||
export const NX_PACKAGE_PROVIDERS: Provider[] = [
|
export const NX_PACKAGE_PROVIDERS: Provider[] = [
|
||||||
NX_PACKAGE_TRANSFORM,
|
NX_PACKAGE_TRANSFORM,
|
||||||
DEFAULT_OPTIONS_PROVIDER,
|
NX_DEFAULT_OPTIONS_PROVIDER,
|
||||||
INIT_TS_CONFIG_TRANSFORM,
|
INIT_TS_CONFIG_TRANSFORM,
|
||||||
ANALYSE_SOURCES_TRANSFORM,
|
ANALYSE_SOURCES_TRANSFORM,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -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,362 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr source.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Added the filePath parameter to the cache key.
|
||||||
|
* - Refactored caching to take into account TailwindCSS processing.
|
||||||
|
* - Added PostCSS plugins needed to support TailwindCSS.
|
||||||
|
* - Added watch mode parameter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as browserslist from 'browserslist';
|
||||||
|
import { sync } from 'find-parent-dir';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { EsbuildExecutor } from 'ng-packagr/lib/esbuild/esbuild-executor';
|
||||||
|
import {
|
||||||
|
generateKey,
|
||||||
|
readCacheEntry,
|
||||||
|
saveCacheEntry,
|
||||||
|
} from 'ng-packagr/lib/utils/cache';
|
||||||
|
import * as log from 'ng-packagr/lib/utils/log';
|
||||||
|
import { dirname, extname, resolve } from 'path';
|
||||||
|
import * as postcssPresetEnv from 'postcss-preset-env';
|
||||||
|
import * as postcssUrl from 'postcss-url';
|
||||||
|
import {
|
||||||
|
getTailwindPostCssPlugins,
|
||||||
|
getTailwindSetup,
|
||||||
|
tailwindDirectives,
|
||||||
|
TailwindSetup,
|
||||||
|
} from '../../../utilities/tailwindcss';
|
||||||
|
|
||||||
|
const postcss = require('postcss');
|
||||||
|
|
||||||
|
export enum CssUrl {
|
||||||
|
inline = 'inline',
|
||||||
|
none = 'none',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum InlineStyleLanguage {
|
||||||
|
sass = 'sass',
|
||||||
|
scss = 'scss',
|
||||||
|
css = 'css',
|
||||||
|
less = 'less',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Result {
|
||||||
|
css: string;
|
||||||
|
warnings: string[];
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StylesheetProcessor {
|
||||||
|
private browserslistData: string[];
|
||||||
|
private targets: string[];
|
||||||
|
private postCssProcessor: ReturnType<typeof postcss>;
|
||||||
|
private esbuild = new EsbuildExecutor();
|
||||||
|
private tailwindSetup: TailwindSetup | undefined;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly basePath: string,
|
||||||
|
private readonly cssUrl?: CssUrl,
|
||||||
|
private readonly styleIncludePaths?: string[],
|
||||||
|
private readonly cacheDirectory?: string | false,
|
||||||
|
private readonly watch?: boolean,
|
||||||
|
private readonly tailwindConfig?: string
|
||||||
|
) {
|
||||||
|
// By default, browserslist defaults are too inclusive
|
||||||
|
// https://github.com/browserslist/browserslist/blob/83764ea81ffaa39111c204b02c371afa44a4ff07/index.js#L516-L522
|
||||||
|
|
||||||
|
// We change the default query to browsers that Angular support.
|
||||||
|
// https://angular.io/guide/browser-support
|
||||||
|
(browserslist.defaults as string[]) = [
|
||||||
|
'last 1 Chrome version',
|
||||||
|
'last 1 Firefox version',
|
||||||
|
'last 2 Edge major versions',
|
||||||
|
'last 2 Safari major versions',
|
||||||
|
'last 2 iOS major versions',
|
||||||
|
'Firefox ESR',
|
||||||
|
];
|
||||||
|
|
||||||
|
this.browserslistData = browserslist(undefined, { path: this.basePath });
|
||||||
|
this.targets = transformSupportedBrowsersToTargets(this.browserslistData);
|
||||||
|
this.tailwindSetup = getTailwindSetup(this.basePath, this.tailwindConfig);
|
||||||
|
this.postCssProcessor = this.createPostCssPlugins();
|
||||||
|
}
|
||||||
|
|
||||||
|
async process({
|
||||||
|
filePath,
|
||||||
|
content,
|
||||||
|
}: {
|
||||||
|
filePath: string;
|
||||||
|
content: string;
|
||||||
|
}): Promise<string> {
|
||||||
|
let key: string | undefined;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.cacheDirectory &&
|
||||||
|
!content.includes('@import') &&
|
||||||
|
!content.includes('@use') &&
|
||||||
|
!this.containsTailwindDirectives(content)
|
||||||
|
) {
|
||||||
|
// No transitive deps and no Tailwind directives, we can cache more aggressively.
|
||||||
|
key = await generateKey(content, ...this.browserslistData, filePath);
|
||||||
|
const result = await readCacheEntry(this.cacheDirectory, 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);
|
||||||
|
|
||||||
|
let containsTailwindDirectives = false;
|
||||||
|
if (this.cacheDirectory) {
|
||||||
|
containsTailwindDirectives = this.containsTailwindDirectives(renderedCss);
|
||||||
|
if (!containsTailwindDirectives) {
|
||||||
|
// No Tailwind directives to process by PostCSS, we can return cached results
|
||||||
|
if (!key) {
|
||||||
|
key = await generateKey(
|
||||||
|
renderedCss,
|
||||||
|
...this.browserslistData,
|
||||||
|
filePath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedResult = await this.getCachedResult(key);
|
||||||
|
if (cachedResult) {
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render postcss (autoprefixing and friends)
|
||||||
|
const result = await this.postCssProcessor.process(renderedCss, {
|
||||||
|
from: filePath,
|
||||||
|
to: filePath.replace(extname(filePath), '.css'),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.cacheDirectory && containsTailwindDirectives) {
|
||||||
|
// We had Tailwind directives to process by PostCSS, only now
|
||||||
|
// is safe to return cached results
|
||||||
|
key = await generateKey(result.css, ...this.browserslistData, filePath);
|
||||||
|
|
||||||
|
const cachedResult = await this.getCachedResult(key);
|
||||||
|
if (cachedResult) {
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const warnings = result.warnings().map((w) => w.toString());
|
||||||
|
const { code, warnings: esBuildWarnings } = await this.esbuild.transform(
|
||||||
|
result.css,
|
||||||
|
{
|
||||||
|
loader: 'css',
|
||||||
|
minify: true,
|
||||||
|
target: this.targets,
|
||||||
|
sourcefile: filePath,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (esBuildWarnings.length > 0) {
|
||||||
|
warnings.push(
|
||||||
|
...(await this.esbuild.formatMessages(esBuildWarnings, {
|
||||||
|
kind: 'warning',
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cacheDirectory) {
|
||||||
|
await saveCacheEntry(
|
||||||
|
this.cacheDirectory,
|
||||||
|
key,
|
||||||
|
JSON.stringify({
|
||||||
|
css: code,
|
||||||
|
warnings,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
warnings.forEach((msg) => log.warn(msg));
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getCachedResult(key: string): Promise<string | undefined> {
|
||||||
|
const cachedResult = await readCacheEntry(
|
||||||
|
this.cacheDirectory as string,
|
||||||
|
key
|
||||||
|
);
|
||||||
|
if (cachedResult) {
|
||||||
|
cachedResult.warnings.forEach((msg) => log.warn(msg));
|
||||||
|
|
||||||
|
return cachedResult.css;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private containsTailwindDirectives(content: string): boolean {
|
||||||
|
return (
|
||||||
|
this.tailwindSetup && tailwindDirectives.some((d) => content.includes(d))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPostCssPlugins(): ReturnType<typeof postcss> {
|
||||||
|
const postCssPlugins = [];
|
||||||
|
if (this.cssUrl !== CssUrl.none) {
|
||||||
|
postCssPlugins.push(postcssUrl({ url: this.cssUrl }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.tailwindSetup) {
|
||||||
|
postCssPlugins.push(
|
||||||
|
...getTailwindPostCssPlugins(
|
||||||
|
this.tailwindSetup,
|
||||||
|
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': {
|
||||||
|
return (await import('sass'))
|
||||||
|
.renderSync({
|
||||||
|
file: filePath,
|
||||||
|
data: css,
|
||||||
|
indentedSyntax: '.sass' === ext,
|
||||||
|
importer: customSassImporter,
|
||||||
|
includePaths: this.styleIncludePaths,
|
||||||
|
})
|
||||||
|
.css.toString();
|
||||||
|
}
|
||||||
|
case '.less': {
|
||||||
|
const { css: content } = await (
|
||||||
|
await import('less')
|
||||||
|
).render(css, {
|
||||||
|
filename: filePath,
|
||||||
|
math: 'always',
|
||||||
|
javascriptEnabled: true,
|
||||||
|
paths: this.styleIncludePaths,
|
||||||
|
});
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
case '.styl':
|
||||||
|
case '.stylus': {
|
||||||
|
const stylus = (await import('stylus')).default;
|
||||||
|
|
||||||
|
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 transformSupportedBrowsersToTargets(
|
||||||
|
supportedBrowsers: string[]
|
||||||
|
): string[] {
|
||||||
|
const transformed: string[] = [];
|
||||||
|
|
||||||
|
// https://esbuild.github.io/api/#target
|
||||||
|
const esBuildSupportedBrowsers = new Set([
|
||||||
|
'safari',
|
||||||
|
'firefox',
|
||||||
|
'edge',
|
||||||
|
'chrome',
|
||||||
|
'ios',
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (const browser of supportedBrowsers) {
|
||||||
|
let [browserName, version] = browser.split(' ');
|
||||||
|
|
||||||
|
// browserslist uses the name `ios_saf` for iOS Safari whereas esbuild uses `ios`
|
||||||
|
if (browserName === 'ios_saf') {
|
||||||
|
browserName = 'ios';
|
||||||
|
// browserslist also uses ranges for iOS Safari versions but only the lowest is required
|
||||||
|
// to perform minimum supported feature checks. esbuild also expects a single version.
|
||||||
|
[version] = version.split('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browserName === 'ie') {
|
||||||
|
transformed.push('edge12');
|
||||||
|
} else if (esBuildSupportedBrowsers.has(browserName)) {
|
||||||
|
if (browserName === 'safari' && version === 'TP') {
|
||||||
|
// esbuild only supports numeric versions so `TP` is converted to a high number (999) since
|
||||||
|
// a Technology Preview (TP) of Safari is assumed to support all currently known features.
|
||||||
|
version = '999';
|
||||||
|
}
|
||||||
|
|
||||||
|
transformed.push(browserName + version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformed.length ? transformed : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function customSassImporter(
|
||||||
|
url: string,
|
||||||
|
prev: string
|
||||||
|
): { file: string; prev: string } | undefined {
|
||||||
|
// NB: Sass importer should always be sync as otherwise it will cause
|
||||||
|
// sass to go in the async path which is slower.
|
||||||
|
if (url[0] !== '~') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = resolveImport(url.substring(1), prev);
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
file: result,
|
||||||
|
prev,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveImport(target: string, basePath: string): string | undefined {
|
||||||
|
const root = sync(basePath, 'node_modules');
|
||||||
|
if (!root) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = resolve(root, 'node_modules', target);
|
||||||
|
if (existsSync(filePath) || existsSync(dirname(filePath))) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveImport(target, dirname(root));
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
jest.mock('@nrwl/workspace/src/core/project-graph');
|
jest.mock('@nrwl/workspace/src/core/project-graph');
|
||||||
jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils');
|
jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils');
|
||||||
jest.mock('ng-packagr');
|
jest.mock('ng-packagr');
|
||||||
|
jest.mock('./ng-packagr-adjustments/ng-package/options.di');
|
||||||
|
|
||||||
import type { ExecutorContext } from '@nrwl/devkit';
|
import type { ExecutorContext } from '@nrwl/devkit';
|
||||||
import * as buildableLibsUtils from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
import * as buildableLibsUtils from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
@ -8,11 +9,12 @@ import * as ngPackagr from 'ng-packagr';
|
|||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import type { BuildAngularLibraryExecutorOptions } from '../package/schema';
|
import type { BuildAngularLibraryExecutorOptions } from '../package/schema';
|
||||||
import { NX_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
import { NX_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
||||||
|
import { nxProvideOptions } from './ng-packagr-adjustments/ng-package/options.di';
|
||||||
import {
|
import {
|
||||||
NX_PACKAGE_PROVIDERS,
|
NX_PACKAGE_PROVIDERS,
|
||||||
NX_PACKAGE_TRANSFORM,
|
NX_PACKAGE_TRANSFORM,
|
||||||
} from './ng-packagr-adjustments/ng-package/package.di';
|
} from './ng-packagr-adjustments/ng-package/package.di';
|
||||||
import ngPackagrLiteExecutor from './ng-packagr-lite.impl';
|
import { ngPackagrLiteExecutor } from './ng-packagr-lite.impl';
|
||||||
|
|
||||||
describe('NgPackagrLite executor', () => {
|
describe('NgPackagrLite executor', () => {
|
||||||
let context: ExecutorContext;
|
let context: ExecutorContext;
|
||||||
@ -83,12 +85,24 @@ describe('NgPackagrLite executor', () => {
|
|||||||
(
|
(
|
||||||
buildableLibsUtils.checkDependentProjectsHaveBeenBuilt as jest.Mock
|
buildableLibsUtils.checkDependentProjectsHaveBeenBuilt as jest.Mock
|
||||||
).mockReturnValue(true);
|
).mockReturnValue(true);
|
||||||
|
const extraOptions: Partial<BuildAngularLibraryExecutorOptions> = {
|
||||||
|
tailwindConfig: 'path/to/tailwind.config.js',
|
||||||
|
watch: false,
|
||||||
|
};
|
||||||
|
const nxProvideOptionsResult = { ...extraOptions, cacheEnabled: true };
|
||||||
|
(nxProvideOptions as jest.Mock).mockImplementation(
|
||||||
|
() => nxProvideOptionsResult
|
||||||
|
);
|
||||||
|
|
||||||
const result = await ngPackagrLiteExecutor(options, context).next();
|
const result = await ngPackagrLiteExecutor(
|
||||||
|
{ ...options, ...extraOptions },
|
||||||
|
context
|
||||||
|
).next();
|
||||||
|
|
||||||
expect(ngPackagr.NgPackagr).toHaveBeenCalledWith([
|
expect(ngPackagr.NgPackagr).toHaveBeenCalledWith([
|
||||||
...NX_PACKAGE_PROVIDERS,
|
...NX_PACKAGE_PROVIDERS,
|
||||||
...NX_ENTRY_POINT_PROVIDERS,
|
...NX_ENTRY_POINT_PROVIDERS,
|
||||||
|
nxProvideOptionsResult,
|
||||||
]);
|
]);
|
||||||
expect(ngPackagrWithBuildTransformMock).toHaveBeenCalledWith(
|
expect(ngPackagrWithBuildTransformMock).toHaveBeenCalledWith(
|
||||||
NX_PACKAGE_TRANSFORM.provide
|
NX_PACKAGE_TRANSFORM.provide
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { resolve } from 'path';
|
|||||||
import { createLibraryExecutor } from '../package/package.impl';
|
import { createLibraryExecutor } from '../package/package.impl';
|
||||||
import type { BuildAngularLibraryExecutorOptions } from '../package/schema';
|
import type { BuildAngularLibraryExecutorOptions } from '../package/schema';
|
||||||
import { NX_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
import { NX_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
||||||
|
import { nxProvideOptions } from './ng-packagr-adjustments/ng-package/options.di';
|
||||||
import {
|
import {
|
||||||
NX_PACKAGE_PROVIDERS,
|
NX_PACKAGE_PROVIDERS,
|
||||||
NX_PACKAGE_TRANSFORM,
|
NX_PACKAGE_TRANSFORM,
|
||||||
@ -22,6 +23,10 @@ async function initializeNgPackgrLite(
|
|||||||
// Add default providers to this list.
|
// Add default providers to this list.
|
||||||
...NX_PACKAGE_PROVIDERS,
|
...NX_PACKAGE_PROVIDERS,
|
||||||
...NX_ENTRY_POINT_PROVIDERS,
|
...NX_ENTRY_POINT_PROVIDERS,
|
||||||
|
nxProvideOptions({
|
||||||
|
tailwindConfig: options.tailwindConfig,
|
||||||
|
watch: options.watch,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
packager.forProject(resolve(context.root, options.project));
|
packager.forProject(resolve(context.root, options.project));
|
||||||
packager.withBuildTransform(NX_PACKAGE_TRANSFORM.provide);
|
packager.withBuildTransform(NX_PACKAGE_TRANSFORM.provide);
|
||||||
|
|||||||
@ -28,6 +28,10 @@
|
|||||||
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
|
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
|
||||||
"enum": ["dependencies", "peerDependencies"],
|
"enum": ["dependencies", "peerDependencies"],
|
||||||
"default": "peerDependencies"
|
"default": "peerDependencies"
|
||||||
|
},
|
||||||
|
"tailwindConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -13,11 +13,11 @@ import {
|
|||||||
provideTransform,
|
provideTransform,
|
||||||
TransformProvider,
|
TransformProvider,
|
||||||
} from 'ng-packagr/lib/graph/transform.di';
|
} from 'ng-packagr/lib/graph/transform.di';
|
||||||
import { OPTIONS_TOKEN } from 'ng-packagr/lib/ng-package/options.di';
|
|
||||||
import {
|
import {
|
||||||
NX_STYLESHEET_PROCESSOR,
|
NX_STYLESHEET_PROCESSOR,
|
||||||
NX_STYLESHEET_PROCESSOR_TOKEN,
|
NX_STYLESHEET_PROCESSOR_TOKEN,
|
||||||
} from '../../styles/stylesheet-processor.di';
|
} from '../../styles/stylesheet-processor.di';
|
||||||
|
import { NX_OPTIONS_TOKEN } from '../options.di';
|
||||||
import { compileNgcTransformFactory } from './compile-ngc.transform';
|
import { compileNgcTransformFactory } from './compile-ngc.transform';
|
||||||
|
|
||||||
export const NX_COMPILE_NGC_TOKEN = new InjectionToken<Transform>(
|
export const NX_COMPILE_NGC_TOKEN = new InjectionToken<Transform>(
|
||||||
@ -27,7 +27,7 @@ export const NX_COMPILE_NGC_TOKEN = new InjectionToken<Transform>(
|
|||||||
export const NX_COMPILE_NGC_TRANSFORM: TransformProvider = provideTransform({
|
export const NX_COMPILE_NGC_TRANSFORM: TransformProvider = provideTransform({
|
||||||
provide: NX_COMPILE_NGC_TOKEN,
|
provide: NX_COMPILE_NGC_TOKEN,
|
||||||
useFactory: compileNgcTransformFactory,
|
useFactory: compileNgcTransformFactory,
|
||||||
deps: [NX_STYLESHEET_PROCESSOR_TOKEN, OPTIONS_TOKEN],
|
deps: [NX_STYLESHEET_PROCESSOR_TOKEN, NX_OPTIONS_TOKEN],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const NX_COMPILE_NGC_PROVIDERS: Provider[] = [
|
export const NX_COMPILE_NGC_PROVIDERS: Provider[] = [
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import {
|
|||||||
isEntryPoint,
|
isEntryPoint,
|
||||||
isEntryPointInProgress,
|
isEntryPointInProgress,
|
||||||
} from 'ng-packagr/lib/ng-package/nodes';
|
} 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 { compileSourceFiles } from 'ng-packagr/lib/ngc/compile-source-files';
|
||||||
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
|
import { NgccProcessor } from 'ng-packagr/lib/ngc/ngcc-processor';
|
||||||
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
import { setDependenciesTsConfigPaths } from 'ng-packagr/lib/ts/tsconfig';
|
||||||
@ -23,6 +22,7 @@ import * as ora from 'ora';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor';
|
import { StylesheetProcessor as StylesheetProcessorClass } from '../../styles/stylesheet-processor';
|
||||||
|
import { NgPackagrOptions } from '../options.di';
|
||||||
|
|
||||||
export const compileNgcTransformFactory = (
|
export const compileNgcTransformFactory = (
|
||||||
StylesheetProcessor: typeof StylesheetProcessorClass,
|
StylesheetProcessor: typeof StylesheetProcessorClass,
|
||||||
@ -71,7 +71,8 @@ export const compileNgcTransformFactory = (
|
|||||||
cssUrl,
|
cssUrl,
|
||||||
styleIncludePaths,
|
styleIncludePaths,
|
||||||
options.cacheEnabled && options.cacheDirectory,
|
options.cacheEnabled && options.cacheDirectory,
|
||||||
options.watch
|
options.watch,
|
||||||
|
options.tailwindConfig
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
await compileSourceFiles(
|
await compileSourceFiles(
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Adapted from the original ng-packagr.
|
||||||
|
*
|
||||||
|
* Changes made:
|
||||||
|
* - Use our own options interface to add support for tailwindConfig.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as findCacheDirectory from 'find-cache-dir';
|
||||||
|
import { InjectionToken, Provider, ValueProvider } from 'injection-js';
|
||||||
|
import { NgPackagrOptions as NgPackagrOptionsBase } from 'ng-packagr/lib/ng-package/options.di';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
|
export interface NgPackagrOptions extends NgPackagrOptionsBase {
|
||||||
|
tailwindConfig?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NX_OPTIONS_TOKEN = new InjectionToken<NgPackagrOptions>(
|
||||||
|
`nx.v1.options`
|
||||||
|
);
|
||||||
|
|
||||||
|
export const nxProvideOptions = (
|
||||||
|
options: NgPackagrOptions = {}
|
||||||
|
): ValueProvider => ({
|
||||||
|
provide: NX_OPTIONS_TOKEN,
|
||||||
|
useValue: normalizeOptions(options),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NX_DEFAULT_OPTIONS_PROVIDER: Provider = nxProvideOptions();
|
||||||
|
|
||||||
|
function normalizeOptions(options: NgPackagrOptions = {}) {
|
||||||
|
const ciEnv = process.env['CI'];
|
||||||
|
const isCI = ciEnv?.toLowerCase() === 'true' || ciEnv === '1';
|
||||||
|
const { cacheEnabled = !isCI, cacheDirectory = findCachePath() } = options;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
cacheEnabled,
|
||||||
|
cacheDirectory,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function findCachePath(): string {
|
||||||
|
const name = 'ng-packagr';
|
||||||
|
|
||||||
|
return findCacheDirectory({ name }) || resolve(tmpdir(), name);
|
||||||
|
}
|
||||||
@ -3,6 +3,8 @@
|
|||||||
*
|
*
|
||||||
* Changes made:
|
* Changes made:
|
||||||
* - Use NX_ENTRY_POINT_TRANSFORM_TOKEN instead of ENTRY_POINT_TRANSFORM_TOKEN.
|
* - Use NX_ENTRY_POINT_TRANSFORM_TOKEN instead of ENTRY_POINT_TRANSFORM_TOKEN.
|
||||||
|
* - USE NX_OPTIONS_TOKEN instead of OPTIONS_TOKEN.
|
||||||
|
* - USE NX_DEFAULT_OPTIONS_PROVIDER instead of DEFAULT_OPTIONS_PROVIDER.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { InjectionToken, Provider } from 'injection-js';
|
import { InjectionToken, Provider } from 'injection-js';
|
||||||
@ -19,13 +21,10 @@ import {
|
|||||||
INIT_TS_CONFIG_TOKEN,
|
INIT_TS_CONFIG_TOKEN,
|
||||||
INIT_TS_CONFIG_TRANSFORM,
|
INIT_TS_CONFIG_TRANSFORM,
|
||||||
} from 'ng-packagr/lib/ng-package/entry-point/init-tsconfig.di';
|
} 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 { packageTransformFactory } from 'ng-packagr/lib/ng-package/package.transform';
|
||||||
import { PROJECT_TOKEN } from 'ng-packagr/lib/project.di';
|
import { PROJECT_TOKEN } from 'ng-packagr/lib/project.di';
|
||||||
import { NX_ENTRY_POINT_TRANSFORM_TOKEN } from './entry-point/entry-point.di';
|
import { NX_ENTRY_POINT_TRANSFORM_TOKEN } from './entry-point/entry-point.di';
|
||||||
|
import { NX_DEFAULT_OPTIONS_PROVIDER, NX_OPTIONS_TOKEN } from './options.di';
|
||||||
|
|
||||||
export const NX_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
export const NX_PACKAGE_TRANSFORM_TOKEN = new InjectionToken<Transform>(
|
||||||
`nx.v1.packageTransform`
|
`nx.v1.packageTransform`
|
||||||
@ -36,7 +35,7 @@ export const NX_PACKAGE_TRANSFORM: TransformProvider = provideTransform({
|
|||||||
useFactory: packageTransformFactory,
|
useFactory: packageTransformFactory,
|
||||||
deps: [
|
deps: [
|
||||||
PROJECT_TOKEN,
|
PROJECT_TOKEN,
|
||||||
OPTIONS_TOKEN,
|
NX_OPTIONS_TOKEN,
|
||||||
INIT_TS_CONFIG_TOKEN,
|
INIT_TS_CONFIG_TOKEN,
|
||||||
ANALYSE_SOURCES_TOKEN,
|
ANALYSE_SOURCES_TOKEN,
|
||||||
NX_ENTRY_POINT_TRANSFORM_TOKEN,
|
NX_ENTRY_POINT_TRANSFORM_TOKEN,
|
||||||
@ -45,7 +44,7 @@ export const NX_PACKAGE_TRANSFORM: TransformProvider = provideTransform({
|
|||||||
|
|
||||||
export const NX_PACKAGE_PROVIDERS: Provider[] = [
|
export const NX_PACKAGE_PROVIDERS: Provider[] = [
|
||||||
NX_PACKAGE_TRANSFORM,
|
NX_PACKAGE_TRANSFORM,
|
||||||
DEFAULT_OPTIONS_PROVIDER,
|
NX_DEFAULT_OPTIONS_PROVIDER,
|
||||||
INIT_TS_CONFIG_TRANSFORM,
|
INIT_TS_CONFIG_TRANSFORM,
|
||||||
ANALYSE_SOURCES_TRANSFORM,
|
ANALYSE_SOURCES_TRANSFORM,
|
||||||
];
|
];
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import {
|
|||||||
getTailwindSetup,
|
getTailwindSetup,
|
||||||
tailwindDirectives,
|
tailwindDirectives,
|
||||||
TailwindSetup,
|
TailwindSetup,
|
||||||
} from '../utilities/tailwindcss';
|
} from '../../../utilities/tailwindcss';
|
||||||
|
|
||||||
const postcss = require('postcss');
|
const postcss = require('postcss');
|
||||||
|
|
||||||
@ -60,7 +60,8 @@ export class StylesheetProcessor {
|
|||||||
private readonly cssUrl?: CssUrl,
|
private readonly cssUrl?: CssUrl,
|
||||||
private readonly styleIncludePaths?: string[],
|
private readonly styleIncludePaths?: string[],
|
||||||
private readonly cacheDirectory?: string | false,
|
private readonly cacheDirectory?: string | false,
|
||||||
private readonly watch?: boolean
|
private readonly watch?: boolean,
|
||||||
|
private readonly tailwindConfig?: string
|
||||||
) {
|
) {
|
||||||
// By default, browserslist defaults are too inclusive
|
// By default, browserslist defaults are too inclusive
|
||||||
// https://github.com/browserslist/browserslist/blob/83764ea81ffaa39111c204b02c371afa44a4ff07/index.js#L516-L522
|
// https://github.com/browserslist/browserslist/blob/83764ea81ffaa39111c204b02c371afa44a4ff07/index.js#L516-L522
|
||||||
@ -78,7 +79,7 @@ export class StylesheetProcessor {
|
|||||||
|
|
||||||
this.browserslistData = browserslist(undefined, { path: this.basePath });
|
this.browserslistData = browserslist(undefined, { path: this.basePath });
|
||||||
this.targets = transformSupportedBrowsersToTargets(this.browserslistData);
|
this.targets = transformSupportedBrowsersToTargets(this.browserslistData);
|
||||||
this.tailwindSetup = getTailwindSetup(this.basePath);
|
this.tailwindSetup = getTailwindSetup(this.basePath, this.tailwindConfig);
|
||||||
this.postCssProcessor = this.createPostCssPlugins();
|
this.postCssProcessor = this.createPostCssPlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
jest.mock('@nrwl/workspace/src/core/project-graph');
|
jest.mock('@nrwl/workspace/src/core/project-graph');
|
||||||
jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils');
|
jest.mock('@nrwl/workspace/src/utilities/buildable-libs-utils');
|
||||||
jest.mock('ng-packagr');
|
jest.mock('ng-packagr');
|
||||||
|
jest.mock('./ng-packagr-adjustments/ng-package/options.di');
|
||||||
|
|
||||||
import type { ExecutorContext } from '@nrwl/devkit';
|
import type { ExecutorContext } from '@nrwl/devkit';
|
||||||
import * as buildableLibsUtils from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
import * as buildableLibsUtils from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
import * as ngPackagr from 'ng-packagr';
|
import * as ngPackagr from 'ng-packagr';
|
||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { NX_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
import { NX_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
||||||
|
import { nxProvideOptions } from './ng-packagr-adjustments/ng-package/options.di';
|
||||||
import {
|
import {
|
||||||
NX_PACKAGE_PROVIDERS,
|
NX_PACKAGE_PROVIDERS,
|
||||||
NX_PACKAGE_TRANSFORM,
|
NX_PACKAGE_TRANSFORM,
|
||||||
} from './ng-packagr-adjustments/ng-package/package.di';
|
} from './ng-packagr-adjustments/ng-package/package.di';
|
||||||
import packageExecutor from './package.impl';
|
import { packageExecutor } from './package.impl';
|
||||||
import type { BuildAngularLibraryExecutorOptions } from './schema';
|
import type { BuildAngularLibraryExecutorOptions } from './schema';
|
||||||
|
|
||||||
describe('Package executor', () => {
|
describe('Package executor', () => {
|
||||||
@ -82,12 +84,24 @@ describe('Package executor', () => {
|
|||||||
(
|
(
|
||||||
buildableLibsUtils.checkDependentProjectsHaveBeenBuilt as jest.Mock
|
buildableLibsUtils.checkDependentProjectsHaveBeenBuilt as jest.Mock
|
||||||
).mockReturnValue(true);
|
).mockReturnValue(true);
|
||||||
|
const extraOptions: Partial<BuildAngularLibraryExecutorOptions> = {
|
||||||
|
tailwindConfig: 'path/to/tailwind.config.js',
|
||||||
|
watch: false,
|
||||||
|
};
|
||||||
|
const nxProvideOptionsResult = { ...extraOptions, cacheEnabled: true };
|
||||||
|
(nxProvideOptions as jest.Mock).mockImplementation(
|
||||||
|
() => nxProvideOptionsResult
|
||||||
|
);
|
||||||
|
|
||||||
const result = await packageExecutor(options, context).next();
|
const result = await packageExecutor(
|
||||||
|
{ ...options, ...extraOptions },
|
||||||
|
context
|
||||||
|
).next();
|
||||||
|
|
||||||
expect(ngPackagr.NgPackagr).toHaveBeenCalledWith([
|
expect(ngPackagr.NgPackagr).toHaveBeenCalledWith([
|
||||||
...NX_PACKAGE_PROVIDERS,
|
...NX_PACKAGE_PROVIDERS,
|
||||||
...NX_ENTRY_POINT_PROVIDERS,
|
...NX_ENTRY_POINT_PROVIDERS,
|
||||||
|
nxProvideOptionsResult,
|
||||||
]);
|
]);
|
||||||
expect(ngPackagrWithBuildTransformMock).toHaveBeenCalledWith(
|
expect(ngPackagrWithBuildTransformMock).toHaveBeenCalledWith(
|
||||||
NX_PACKAGE_TRANSFORM.provide
|
NX_PACKAGE_TRANSFORM.provide
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
import type { ExecutorContext } from '@nrwl/devkit';
|
import type { ExecutorContext } from '@nrwl/devkit';
|
||||||
import { readCachedProjectGraph } from '@nrwl/workspace/src/core/project-graph';
|
import { readCachedProjectGraph } from '@nrwl/workspace/src/core/project-graph';
|
||||||
import {
|
|
||||||
createTmpTsConfig,
|
|
||||||
DependentBuildableProjectNode,
|
|
||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
|
||||||
import {
|
import {
|
||||||
calculateProjectDependencies,
|
calculateProjectDependencies,
|
||||||
checkDependentProjectsHaveBeenBuilt,
|
checkDependentProjectsHaveBeenBuilt,
|
||||||
|
createTmpTsConfig,
|
||||||
|
DependentBuildableProjectNode,
|
||||||
updateBuildableProjectPackageJsonDependencies,
|
updateBuildableProjectPackageJsonDependencies,
|
||||||
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
} from '@nrwl/workspace/src/utilities/buildable-libs-utils';
|
||||||
import type { NgPackagr } from 'ng-packagr';
|
import type { NgPackagr } from 'ng-packagr';
|
||||||
@ -15,6 +13,7 @@ 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_ENTRY_POINT_PROVIDERS } from './ng-packagr-adjustments/ng-package/entry-point/entry-point.di';
|
||||||
|
import { nxProvideOptions } from './ng-packagr-adjustments/ng-package/options.di';
|
||||||
import {
|
import {
|
||||||
NX_PACKAGE_PROVIDERS,
|
NX_PACKAGE_PROVIDERS,
|
||||||
NX_PACKAGE_TRANSFORM,
|
NX_PACKAGE_TRANSFORM,
|
||||||
@ -29,6 +28,10 @@ async function initializeNgPackagr(
|
|||||||
const packager = new (await import('ng-packagr')).NgPackagr([
|
const packager = new (await import('ng-packagr')).NgPackagr([
|
||||||
...NX_PACKAGE_PROVIDERS,
|
...NX_PACKAGE_PROVIDERS,
|
||||||
...NX_ENTRY_POINT_PROVIDERS,
|
...NX_ENTRY_POINT_PROVIDERS,
|
||||||
|
nxProvideOptions({
|
||||||
|
tailwindConfig: options.tailwindConfig,
|
||||||
|
watch: options.watch,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
packager.forProject(resolve(context.root, options.project));
|
packager.forProject(resolve(context.root, options.project));
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
export interface BuildAngularLibraryExecutorOptions {
|
export interface BuildAngularLibraryExecutorOptions {
|
||||||
project: string;
|
project: string;
|
||||||
tsConfig?: string;
|
|
||||||
buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies';
|
buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies';
|
||||||
|
tailwindConfig?: string;
|
||||||
|
tsConfig?: string;
|
||||||
updateBuildableProjectDepsInPackageJson?: boolean;
|
updateBuildableProjectDepsInPackageJson?: boolean;
|
||||||
watch?: boolean;
|
watch?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,10 @@
|
|||||||
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
|
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
|
||||||
"enum": ["dependencies", "peerDependencies"],
|
"enum": ["dependencies", "peerDependencies"],
|
||||||
"default": "peerDependencies"
|
"default": "peerDependencies"
|
||||||
|
},
|
||||||
|
"tailwindConfig": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The full path for the Tailwind configuration file, relative to the workspace root. If not provided and a `tailwind.config.js` file exists in the project or workspace root, it will be used. Otherwise, Tailwind will not be configured."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -18,15 +18,21 @@ export const tailwindDirectives = [
|
|||||||
'@screen',
|
'@screen',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getTailwindSetup(basePath: string): TailwindSetup | undefined {
|
export function getTailwindSetup(
|
||||||
// Try to find TailwindCSS configuration file in the project or workspace root.
|
basePath: string,
|
||||||
const tailwindConfigFile = 'tailwind.config.js';
|
tailwindConfig?: string
|
||||||
let tailwindConfigPath: string | undefined;
|
): TailwindSetup | undefined {
|
||||||
for (const path of [basePath, appRootPath]) {
|
let tailwindConfigPath = tailwindConfig;
|
||||||
const fullPath = join(path, tailwindConfigFile);
|
|
||||||
if (existsSync(fullPath)) {
|
if (!tailwindConfigPath) {
|
||||||
tailwindConfigPath = fullPath;
|
// Try to find TailwindCSS configuration file in the project or workspace root.
|
||||||
break;
|
const tailwindConfigFile = 'tailwind.config.js';
|
||||||
|
for (const path of [basePath, appRootPath]) {
|
||||||
|
const fullPath = join(path, tailwindConfigFile);
|
||||||
|
if (existsSync(fullPath)) {
|
||||||
|
tailwindConfigPath = fullPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user