Skip to content

Commit

Permalink
Merge pull request #1997 from sass/merge-main
Browse files Browse the repository at this point in the history
Merge origin/main into feature.color-4
  • Loading branch information
nex3 committed May 30, 2024
2 parents 12fabe2 + 037a4da commit 96188b7
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 121 deletions.
6 changes: 6 additions & 0 deletions js-api-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ if (!fs.existsSync(specIndex)) {
// `node_modules` directory.
fs.copySync(p.resolve(specPath), p.join(sassPackagePath, 'js-api'));

// Copy deprecations YAML so we can test against it.
fs.copySync(
p.resolve(p.join(argv.sassSassRepo, 'spec/deprecations.yaml')),
p.join(sassPackagePath, 'deprecations.yaml')
);

fs.writeFileSync(
p.join(sassPackagePath, 'package.json'),
JSON.stringify({
Expand Down
68 changes: 68 additions & 0 deletions js-api-spec/deprecations.node.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2024 Google LLC. Use of this source code is governed by an
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import fs from 'fs';
import yaml from 'js-yaml';
import {deprecations, Deprecation, Version} from 'sass';

describe('deprecation type', () => {
const deprecationsMap = deprecations as unknown as {
[key: string]: Deprecation;
};
const obsoleteDeprecations: {[key: string]: [string, string]} = {};
const activeDeprecations: {[key: string]: string} = {};
const futureDeprecations: Set<string> = new Set();
const data = yaml.load(
fs.readFileSync('js-api-spec/node_modules/sass/deprecations.yaml', 'utf8')
) as {
[key: string]: {
'dart-sass':
| {status: 'future'}
| {status: 'active'; deprecated: string}
| {status: 'obsolete'; deprecated: string; obsolete: string};
};
};
for (const [id, deprecation] of Object.entries(data)) {
const dartSass = deprecation['dart-sass'];
if (dartSass.status === 'obsolete') {
obsoleteDeprecations[id] = [dartSass.deprecated, dartSass.obsolete];
} else if (dartSass.status === 'active') {
activeDeprecations[id] = dartSass.deprecated;
} else if (dartSass.status === 'future') {
futureDeprecations.add(id);
}
}

// These tests assume that the JS API being tested is backed by Dart Sass.
// If there's a JS API implementation in the future with a different compiler,
// then these tests shouldn't be run.
for (const [id, versions] of Object.entries(obsoleteDeprecations)) {
if (!versions) continue;
const [deprecatedIn, obsoleteIn] = versions;
it(`${id} deprecated in ${deprecatedIn} and obsolete in ${obsoleteIn}`, () => {
const deprecation = deprecationsMap[id];
expect(deprecation?.id).toBe(id);
expect(deprecation?.status).toBe('obsolete');
expect(deprecation?.deprecatedIn).toEqual(Version.parse(deprecatedIn));
expect(deprecation?.obsoleteIn).toEqual(Version.parse(obsoleteIn));
});
}

for (const [id, version] of Object.entries(activeDeprecations)) {
it(`${id} deprecated in ${version}`, () => {
const deprecation = deprecationsMap[id];
expect(deprecation?.id).toBe(id);
expect(deprecation?.status).toBe('active');
expect(deprecation?.deprecatedIn).toEqual(Version.parse(version));
});
}

for (const id of futureDeprecations) {
it(`${id} is a future deprecation`, () => {
const deprecation = deprecationsMap[id];
expect(deprecation?.id).toBe(id);
expect(deprecation?.status).toBe('future');
});
}
});
104 changes: 0 additions & 104 deletions js-api-spec/deprecations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,116 +6,12 @@ import {
compileString,
deprecations,
Deprecation,
Deprecations,
Importer,
Version,
} from 'sass';

import {captureStdio, URL} from './utils';

/**
* Map from obsolete deprecation IDs to version pairs.
*
* The first version is the version this deprecation type was deprecated in,
* while the second version is the version it was made obsolete in.
*/
const obsoleteDeprecations: {[key in keyof Deprecations]?: [string, string]} =
{};

/** Map from active deprecation IDs to the version they were deprecated in. */
const activeDeprecations: {[key in keyof Deprecations]?: string} = {
'call-string': '0.0.0',
elseif: '1.3.2',
'moz-document': '1.7.2',
'relative-canonical': '1.14.2',
'new-global': '1.17.2',
'color-module-compat': '1.23.0',
'slash-div': '1.33.0',
'bogus-combinators': '1.54.0',
'strict-unary': '1.55.0',
'function-units': '1.56.0',
'duplicate-var-flags': '1.62.0',
'null-alpha': '1.62.3',
'abs-percent': '1.65.0',
'fs-importer-cwd': '1.73.0',
'color-4-api': '1.76.0',
'color-functions': '1.76.0',
};

/**
* List of future deprecation IDs.
*
* This is only structured as an object to allow us to use a mapped object type
* to ensure that all deprecation IDs listed here are included in the JS API
* spec.
*/
const futureDeprecations: {[key in keyof Deprecations]?: true} = {import: true};

/**
* This is a temporary synchronization check to ensure that any new deprecation
* types are added to all five of these locations:
* - lib/src/deprecation.dart in sass/dart-sass
* - js-api-doc/deprecations.d.ts in sass/sass
* - spec/js-api/deprecations.d.ts.md in sass/sass
* - lib/src/deprecations.ts in sass/embedded-host-node
* - js-api-spec/deprecations.test.ts in sass/sass-spec (this file)
*
* Work to replace these manual changes with generated code from a single
* source-of-truth is tracked in sass/sass#3827
*/
it('there are no extra or missing deprecation types', () => {
const expectedDeprecations = [
...Object.keys(obsoleteDeprecations),
...Object.keys(activeDeprecations),
...Object.keys(futureDeprecations),
'user-authored',
];
const actualDeprecations = Object.keys(deprecations);
const extraDeprecations = actualDeprecations.filter(
deprecation => !expectedDeprecations.includes(deprecation)
);
expect(extraDeprecations).toBeEmptyArray();
const missingDeprecations = expectedDeprecations.filter(
deprecation => !actualDeprecations.includes(deprecation)
);
expect(missingDeprecations).toBeEmptyArray();
});

describe('deprecation type', () => {
const deprecationsMap = deprecations as unknown as {
[key: string]: Deprecation;
};

for (const [id, versions] of Object.entries(obsoleteDeprecations)) {
if (!versions) continue;
const [deprecatedIn, obsoleteIn] = versions;
it(`${id} deprecated in ${deprecatedIn} and obsolete in ${obsoleteIn}`, () => {
const deprecation = deprecationsMap[id];
expect(deprecation?.id).toBe(id);
expect(deprecation?.status).toBe('obsolete');
expect(deprecation?.deprecatedIn).toEqual(Version.parse(deprecatedIn));
expect(deprecation?.obsoleteIn).toEqual(Version.parse(obsoleteIn));
});
}

for (const [id, version] of Object.entries(activeDeprecations)) {
it(`${id} deprecated in ${version}`, () => {
const deprecation = deprecationsMap[id];
expect(deprecation?.id).toBe(id);
expect(deprecation?.status).toBe('active');
expect(deprecation?.deprecatedIn).toEqual(Version.parse(version));
});
}

for (const [id] of Object.entries(futureDeprecations)) {
it(`${id} is a future deprecation`, () => {
const deprecation = deprecationsMap[id];
expect(deprecation?.id).toBe(id);
expect(deprecation?.status).toBe('future');
});
}
});

describe('a warning', () => {
it('is emitted with no flags', done => {
compileString('a { $b: c !global; }', {
Expand Down
25 changes: 25 additions & 0 deletions js-api-spec/importer.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from 'sass';

import {sandbox} from './sandbox';
import {spy} from './utils';

it('avoids importer when canonicalize() returns null', () =>
sandbox(dir => {
Expand Down Expand Up @@ -366,6 +367,30 @@ describe('FileImporter', () => {
).toThrowSassException({line: 0});
});
});

// Regression test for sass/dart-sass#2208.
it('imports the same relative url from different base urls as different files', () =>
sandbox(dir => {
const findFileUrl = spy((url, context) => {
return url === 'y' ? new URL('x.scss', context.containingUrl) : null;
});

dir.write({
'main.scss': '@import "sub1/test"; @import "sub1/sub2/test"',
'sub1/test.scss': '@import "y"',
'sub1/x.scss': 'x { from: sub1; }',
'sub1/sub2/test.scss': '@import "y"',
'sub1/sub2/x.scss': 'x { from: sub2; }',
});

expect(
compile(dir('main.scss'), {
importers: [{findFileUrl}],
}).css.toString()
).toEqualIgnoringWhitespace('x { from: sub1; } x { from: sub2; }');

expect(findFileUrl).toHaveBeenCalledTimes(2);
}));
});

it(
Expand Down
30 changes: 30 additions & 0 deletions js-api-spec/legacy/importer.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,36 @@ describe('with contents', () => {
}),
}).stats.includedFiles
).toContain(p.resolve('bar')));

// Regression test for sass/dart-sass#2208.
it('imports the same relative url from different base urls as different files', () =>
sandbox(dir => {
const importer = spy((url: string, prev: string) => {
return url === 'x'
? {
contents: `x {from: ${p.basename(p.dirname(prev))}}`,
file: p.resolve(p.dirname(prev), 'x.scss'),
}
: null;
});

dir.write({
'main.scss': '@import "sub1/test"; @import "sub1/sub2/test"',
'sub1/test.scss': '@import "x"',
'sub1/sub2/test.scss': '@import "x"',
});

expect(
sass
.renderSync({
file: dir('main.scss'),
importer,
})
.css.toString()
).toEqualIgnoringWhitespace('x { from: sub1; } x { from: sub2; }');

expect(importer).toHaveBeenCalledTimes(2);
}));
});

describe('with a file redirect', () => {
Expand Down
45 changes: 45 additions & 0 deletions spec/css/keyframes.hrx
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,48 @@ $a: b;
c: d;
}
}

<===>
================================================================================
<===> in_keyframe_block/unknown_at_rule/input.scss
@keyframes a {
to {@b}
}

<===> in_keyframe_block/unknown_at_rule/output.css
@keyframes a {
to {
@b;
}
}

<===>
================================================================================
<===> in_keyframe_block/known_at_rule/input.scss
@keyframes a {
to {@media screen {b: c}}
}

<===> in_keyframe_block/known_at_rule/output.css
@keyframes a {
to {
@media screen {
b: c;
}
}
}

<===>
================================================================================
<===> error/in_keyframe_block/style_rule/input.scss
@keyframes a {
to {to {c: d}}
}

<===> error/in_keyframe_block/style_rule/error
Error: Style rules may not be used within keyframe blocks.
,
2 | to {to {c: d}}
| ^^^^^^^^^
'
input.scss 2:7 root stylesheet
Loading

0 comments on commit 96188b7

Please sign in to comment.