Skip to content

Commit

Permalink
feat: Allow to fail the build when sass deprecation warnings found (#90)
Browse files Browse the repository at this point in the history
  • Loading branch information
just-boris authored Aug 22, 2024
1 parent 9590d0c commit 09d2509
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 18 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"postcss-inline-svg": "^6.0.0",
"postcss-modules": "^6.0.0",
"prettier": "^2.7.1",
"sass": "^1.49.0",
"sass": "^1.77.8",
"string-hash": "^1.1.3",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.example {
color: red;

a {
font-weight: bold;
}

// top level style after the nested rule to trigger this deprecation warning
// https://sass-lang.com/documentation/breaking-changes/mixed-decls/
font-weight: normal;
}
23 changes: 19 additions & 4 deletions src/build/__tests__/styles.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { expect, test } from 'vitest';
import { buildStyles, InlineStylesheet } from '../internal';
import { buildStyles, InlineStylesheet, BuildStylesOptions } from '../internal';
import { join } from 'node:path';
import { readFileSync, readdirSync } from 'node:fs';

const fixturesRoot = join(__dirname, '__fixtures__', 'scss-only');
const outputRoot = join(__dirname, 'out', 'scss-only');

async function buildWithFixtures(suiteName: string, inlines?: InlineStylesheet[]) {
async function buildWithFixtures(
suiteName: string,
{ inlines, options }: { inlines?: InlineStylesheet[]; options?: BuildStylesOptions } = {}
) {
const outDir = join(outputRoot, suiteName);
await buildStyles(join(fixturesRoot, suiteName), join(outputRoot, suiteName), inlines);
await buildStyles(join(fixturesRoot, suiteName), join(outputRoot, suiteName), inlines, options);
return outDir;
}

Expand Down Expand Up @@ -40,7 +43,9 @@ test('bundles all imports for styles.scss entry point', async () => {
});

test('supports virtual stylesheets', async () => {
const outDir = await buildWithFixtures('inlines', [{ url: 'awsui:tokens', contents: '$color-background: #abcabc' }]);
const outDir = await buildWithFixtures('inlines', {
inlines: [{ url: 'awsui:tokens', contents: '$color-background: #abcabc' }],
});
expect(readdirSync(outDir)).toEqual(['styles.css.js', 'styles.scoped.css', 'styles.selectors.js']);
const cssContent = readFileSync(join(outDir, 'styles.scoped.css'), 'utf8');
expect(cssContent).toContain('#abcabc');
Expand All @@ -51,6 +56,16 @@ test('throws an error if a virtual stylesheet is not defined', async () => {
await expect(() => buildWithFixtures('inlines')).rejects.toThrowError(/awsui:tokens/);
});

test('ignores deprecation warnings by default', async () => {
await expect(buildWithFixtures('mixed-declarations')).resolves.toEqual(expect.any(String));
});

test('throws an error if errors on deprecation warnings are enabled', async () => {
await expect(() =>
buildWithFixtures('mixed-declarations', { options: { failOnDeprecations: true } })
).rejects.toThrowError(/Unexpected deprecation warnings during sass build/);
});

test('mirrors directory structure of the sources', async () => {
const outDir = await buildWithFixtures('dir-structure');
expect(readdirSync(outDir)).toEqual(['styles.css.js', 'styles.scoped.css', 'styles.selectors.js', 'sub-component']);
Expand Down
10 changes: 7 additions & 3 deletions src/build/internal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { buildStyles, InlineStylesheet } from './tasks/style';
import { buildStyles, InlineStylesheet, BuildStylesOptions } from './tasks/style';
import { createPresetFiles } from './tasks/preset';
import { createInternalTokenFiles } from './tasks/internal-tokens';
import { createPublicTokenFiles } from './tasks/public-tokens';
Expand All @@ -9,7 +9,7 @@ import { getInlineStylesheets } from './inline-stylesheets';
import { calculatePropertiesMap } from './properties';
import findNeededTokens from './needed-tokens';

export { buildStyles, InlineStylesheet };
export { buildStyles, InlineStylesheet, BuildStylesOptions };

export type Tasks = 'preset' | 'design-tokens';

Expand Down Expand Up @@ -38,6 +38,8 @@ export interface BuildThemedComponentsInternalParams {
descriptions?: Record<string, string>;
/** Indicates whether to generate a JSON schema for design tokens JSON format and validate against the schema **/
jsonSchema?: boolean;
/** Fail the build when SASS deprecation warning occurs **/
failOnDeprecations?: boolean;
}
/**
* Builds themed components and optionally design tokens, if not skipped.
Expand Down Expand Up @@ -68,6 +70,7 @@ export async function buildThemedComponentsInternal(params: BuildThemedComponent
skip = [],
descriptions = {},
jsonSchema = false,
failOnDeprecations,
} = params;

if (!skip.includes('design-tokens') && !designTokensOutputDir) {
Expand All @@ -83,7 +86,8 @@ export async function buildThemedComponentsInternal(params: BuildThemedComponent
const styleTask = buildStyles(
scssDir,
componentsOutputDir,
getInlineStylesheets(primary, secondary, defaults, variablesMap, propertiesMap, neededTokens)
getInlineStylesheets(primary, secondary, defaults, variablesMap, propertiesMap, neededTokens),
{ failOnDeprecations }
);
const internalTokensTask = createInternalTokenFiles(defaults, propertiesMap, componentsOutputDir);

Expand Down
31 changes: 28 additions & 3 deletions src/build/tasks/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,18 @@ export interface InlineStylesheet {
contents: string;
}

export async function buildStyles(sassDir: string, outputDir: string, inlines: InlineStylesheet[] = []) {
export interface BuildStylesOptions {
failOnDeprecations?: boolean;
}

export async function buildStyles(
sassDir: string,
outputDir: string,
inlines: InlineStylesheet[] = [],
options: BuildStylesOptions = {}
) {
const files = await promisify(glob)('**/styles.scss', { cwd: sassDir });
const compiler = createCompiler(inlines, outputDir, sassDir);
const compiler = createCompiler(inlines, outputDir, sassDir, options);

const promises = files.map((file) => compiler(file));

Expand Down Expand Up @@ -55,15 +64,31 @@ const npmImports = {
},
};

function createCompiler(inlines: InlineStylesheet[], outputDir: string, sassDir: string) {
function createCompiler(inlines: InlineStylesheet[], outputDir: string, sassDir: string, options: BuildStylesOptions) {
const importer = createImporter(inlines);
return async (file: string) => {
const input = path.join(sassDir, file);
const deprecations: Array<string> = [];

const sassResult = sass.compile(input, {
logger: {
warn(message, meta) {
if (meta.deprecation && options.failOnDeprecations) {
deprecations.push(message);
} else {
console.warn(message);
}
},
},
style: 'expanded',
importers: [importer, npmImports],
});
if (deprecations.length > 0) {
for (const deprecation of deprecations) {
console.error(deprecation);
}
throw new Error('Unexpected deprecation warnings during sass build.');
}
const intermediate = path.join(sassDir, rename(file, '.css'));
const postCSSForEachResult = await postCSSForEach(sassDir, outputDir, sassResult.css, intermediate);
const postCSSAfterAllResult = await postCSSAfterAll(postCSSForEachResult.css, intermediate);
Expand Down

0 comments on commit 09d2509

Please sign in to comment.