Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement sass --embedded in pure JS mode #344

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,7 @@ jobs:
working-directory: sass-spec

- name: Compile
run: |
npm run compile
if [[ "$RUNNER_OS" == "Windows" ]]; then
# Avoid copying the entire Dart Sass build directory on Windows,
# since it may contain symlinks that cp will choke on.
mkdir -p dist/lib/src/vendor/dart-sass/
cp {`pwd`/,dist/}lib/src/vendor/dart-sass/sass.bat
cp {`pwd`/,dist/}lib/src/vendor/dart-sass/sass.snapshot
else
ln -s {`pwd`/,dist/}lib/src/vendor/dart-sass
fi
run: npm run compile

- name: Run tests
run: npm run js-api-spec -- --sassPackage .. --sassSassRepo ../language
Expand Down
5 changes: 5 additions & 0 deletions bin/sass.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env node

import * as child_process from 'child_process';
import * as path from 'path';
import {compilerCommand} from '../lib/src/compiler-path';

// TODO npm/cmd-shim#152 and yarnpkg/berry#6422 - If and when the package
Expand All @@ -12,6 +13,10 @@ try {
compilerCommand[0],
[...compilerCommand.slice(1), ...process.argv.slice(2)],
{
// Node blocks launching .bat and .cmd without a shell due to CVE-2024-27980
shell: ['.bat', '.cmd'].includes(
path.extname(compilerCommand[0]).toLowerCase(),
),
stdio: 'inherit',
windowsHide: true,
},
Expand Down
58 changes: 30 additions & 28 deletions lib/src/compiler-path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import * as fs from 'fs';
import * as p from 'path';
import {getElfInterpreter} from './elf';
import {isErrnoException} from './utils';

/**
* Detect if the given binary is linked with musl libc by checking if
Expand All @@ -23,57 +21,61 @@ function isLinuxMusl(path: string): boolean {
}
}

/** The full command for the embedded compiler executable. */
export const compilerCommand = (() => {
/** The module name for the embedded compiler executable. */
export const compilerModule = (() => {
const platform =
process.platform === 'linux' && isLinuxMusl(process.execPath)
? 'linux-musl'
: (process.platform as string);

const arch = process.arch;

// find for development
for (const path of ['vendor', '../../../lib/src/vendor']) {
const executable = p.resolve(
__dirname,
path,
`dart-sass/sass${platform === 'win32' ? '.bat' : ''}`,
);

if (fs.existsSync(executable)) return [executable];
}
return `sass-embedded-${platform}-${arch}`;
})();

/** The full command for the embedded compiler executable. */
export const compilerCommand = (() => {
try {
return [
require.resolve(
`sass-embedded-${platform}-${arch}/dart-sass/src/dart` +
(platform === 'win32' ? '.exe' : ''),
),
require.resolve(
`sass-embedded-${platform}-${arch}/dart-sass/src/sass.snapshot`,
`${compilerModule}/dart-sass/src/dart` +
(process.platform === 'win32' ? '.exe' : ''),
),
require.resolve(`${compilerModule}/dart-sass/src/sass.snapshot`),
];
} catch (ignored) {
// ignored
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
}

try {
return [
require.resolve(
`sass-embedded-${platform}-${arch}/dart-sass/sass` +
(platform === 'win32' ? '.bat' : ''),
`${compilerModule}/dart-sass/sass` +
(process.platform === 'win32' ? '.bat' : ''),
),
];
} catch (e: unknown) {
if (!(isErrnoException(e) && e.code === 'MODULE_NOT_FOUND')) {
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
}

try {
return [
process.execPath,
p.join(p.dirname(require.resolve('sass')), 'sass.js'),
];
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
}

throw new Error(
"Embedded Dart Sass couldn't find the embedded compiler executable. " +
'Please make sure the optional dependency ' +
`sass-embedded-${platform}-${arch} is installed in ` +
'node_modules.',
`Please make sure the optional dependency ${compilerModule} or sass is ` +
'installed in node_modules.',
);
})();
42 changes: 33 additions & 9 deletions tool/get-embedded-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

import {promises as fs} from 'fs';
import * as p from 'path';
import * as shell from 'shelljs';

import {compilerModule} from '../lib/src/compiler-path';
import * as utils from './utils';

/**
Expand All @@ -14,7 +16,7 @@ import * as utils from './utils';
* at `path`. By default, checks out the latest revision from GitHub.
*/
export async function getEmbeddedCompiler(
outPath: string,
js?: boolean,
options?: {ref: string} | {path: string},
): Promise<void> {
const repo = 'dart-sass';
Expand All @@ -41,21 +43,43 @@ export async function getEmbeddedCompiler(
await utils.link(languageInHost, languageInCompiler);
}

buildDartSassEmbedded(source);
await utils.link(p.join(source, 'build'), p.join(outPath, repo));
buildDartSassEmbedded(source, js ?? false);

const jsModulePath = p.resolve('node_modules/sass');
const dartModulePath = p.resolve(p.join('node_modules', compilerModule));
if (js) {
await fs.rm(dartModulePath, {force: true, recursive: true});
await utils.link(p.join(source, 'build/npm'), jsModulePath);
} else {
await fs.rm(jsModulePath, {force: true, recursive: true});
await utils.link(p.join(source, 'build'), p.join(dartModulePath, repo));
}
}

// Builds the Embedded Dart Sass executable from the source at `repoPath`.
function buildDartSassEmbedded(repoPath: string): void {
function buildDartSassEmbedded(repoPath: string, js: boolean): void {
console.log("Downloading Dart Sass's dependencies.");
shell.exec('dart pub upgrade', {
cwd: repoPath,
silent: true,
});

console.log('Building the Dart Sass executable.');
shell.exec('dart run grinder protobuf pkg-standalone-dev', {
cwd: repoPath,
env: {...process.env, UPDATE_SASS_PROTOCOL: 'false'},
});
if (js) {
shell.exec('npm install', {
cwd: repoPath,
silent: true,
});

console.log('Building the Dart Sass npm package.');
shell.exec('dart run grinder protobuf pkg-npm-dev', {
cwd: repoPath,
env: {...process.env, UPDATE_SASS_PROTOCOL: 'false'},
});
} else {
console.log('Building the Dart Sass executable.');
shell.exec('dart run grinder protobuf pkg-standalone-dev', {
cwd: repoPath,
env: {...process.env, UPDATE_SASS_PROTOCOL: 'false'},
});
}
}
10 changes: 7 additions & 3 deletions tool/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const argv = yargs(process.argv.slice(2))
type: 'string',
description: 'Build the Embedded Dart Sass binary from this Git ref.',
})
.option('compiler-js', {
type: 'boolean',
description: 'Build the Embedded Dart Sass with dart2js.',
})
.option('skip-compiler', {
type: 'boolean',
description: "Don't Embedded Dart Sass at all.",
Expand Down Expand Up @@ -55,15 +59,15 @@ void (async () => {

if (!argv['skip-compiler']) {
if (argv['compiler-ref']) {
await getEmbeddedCompiler(outPath, {
await getEmbeddedCompiler(argv['compiler-js'], {
ref: argv['compiler-ref'],
});
} else if (argv['compiler-path']) {
await getEmbeddedCompiler(outPath, {
await getEmbeddedCompiler(argv['compiler-js'], {
path: argv['compiler-path'],
});
} else {
await getEmbeddedCompiler(outPath);
await getEmbeddedCompiler(argv['compiler-js']);
}
}

Expand Down