Skip to content

Commit

Permalink
Implement sass --embedded in pure JS mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ntkme committed Nov 15, 2024
1 parent 5b974cf commit 4744393
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 51 deletions.
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

0 comments on commit 4744393

Please sign in to comment.