Skip to content

Commit

Permalink
wip feat(sass): support remote scss file
Browse files Browse the repository at this point in the history
close #39
  • Loading branch information
3cp committed Jul 24, 2020
1 parent ef85842 commit 2e9a132
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 37 deletions.
4 changes: 2 additions & 2 deletions client/src-worker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import {Container} from 'aurelia-dependency-injection';
methods.forEach(m => patch(m));
})();

const container = new Container();
const session = container.get(DumberSession);
diContainer = new Container();
const session = diContainer.get(DumberSession);

onmessage = async function(event) {
var action = event.data;
Expand Down
22 changes: 12 additions & 10 deletions client/src-worker/transpiler.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import path from 'path';
import {inject} from 'aurelia-dependency-injection';
import {SvelteTranspiler} from './transpilers/svelte';
import {Au2Transpiler} from './transpilers/au2';
import {AuTsTranspiler} from './transpilers/au-ts';
Expand All @@ -7,17 +8,18 @@ import {SassTranspiler} from './transpilers/sass';
import {LessTranspiler} from './transpilers/less';
import {TextTranspiler} from './transpilers/text';

@inject(
SvelteTranspiler,
Au2Transpiler,
AuTsTranspiler,
JsTranspiler,
SassTranspiler,
LessTranspiler,
TextTranspiler
)
export class Transpiler {
constructor() {
this.transpilers = [
new SvelteTranspiler(),
new Au2Transpiler(),
new AuTsTranspiler(),
new JsTranspiler(),
new SassTranspiler(),
new LessTranspiler(),
new TextTranspiler()
];
constructor(...transpilers) {
this.transpilers = transpilers;
}

findTranspiler(file, files) {
Expand Down
123 changes: 108 additions & 15 deletions client/src-worker/transpilers/sass.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,65 @@
import path from 'path';
import {ext, parse, resolveModuleId} from 'dumber-module-loader/dist/id-utils';
import _ from 'lodash';
import {inject} from 'aurelia-dependency-injection';
import {CachePrimitives} from '../cache-primitives';

const EXTS = ['.scss', '.sass'];

function cleanSource(s) {
const idx = s.indexOf('/sass/');
if (idx === -1) return s;
return s.slice(idx + 6);
if (s.startsWith('../sass/')) {
return s.slice(8);
}
if (s.startsWith('../')) {
return s.slice(3);
}
return s;
}

export function possiblePaths(filePath) {
const parsed = parse(filePath);
const [packagePath, ...others] = parsed.parts;
if (others.length === 0) return [];

const base = others.pop();
const dir = _(others).map(o => o + '/').join('');

if (EXTS.indexOf(parsed.ext) !== -1 || parsed.ext === '.css') {
return [
{packagePath, filePath: dir + base},
{packagePath, filePath: dir + base + '/_index.scss'},
{packagePath, filePath: dir + base + '/_index.sass'}
];
}

return [
{packagePath, filePath: dir + base + '.scss'},
{packagePath, filePath: dir + base + '.sass'},
{packagePath, filePath: dir + base + '.css'},
{packagePath, filePath: dir + '_' + base + '.scss'},
{packagePath, filePath: dir + '_' + base + '.sass'},
{packagePath, filePath: dir + base + '/_index.scss'},
{packagePath, filePath: dir + base + '/_index.sass'}
];
}

@inject(CachePrimitives)
export class SassTranspiler {
constructor(primitives) {
this.primitives = primitives;
}

match(file) {
const ext = path.extname(file.filename);
return EXTS.indexOf(ext) !== -1;
const e = ext(file.filename);
return EXTS.indexOf(e) !== -1;
}

async fetchRemoteFile(path) {
for (const {packagePath, filePath} of possiblePaths(path)) {
if (await this.primitives.doesJsdelivrFileExist(packagePath, filePath)) {
return this.primitives.getJsdelivrFile(packagePath, filePath);
}
}
throw new Error('No remote file found for ' + path);
}

_lazyLoad() {
Expand All @@ -21,7 +68,47 @@ export class SassTranspiler {
// https://github.com/sass/dart-sass/issues/25
// So I have to use sass.js (emscripted libsass) as it
// provided a fake fs layer.
this._promise = import('sass.js/dist/sass.sync');
this._promise = import('sass.js/dist/sass.sync').then(Sass => {
// Add custom importer to handle import from npm packages.
Sass.importer((request, done) => {
if (
request.path ||
request.current.startsWith('.') ||
request.current.match(/^https?:\/\//)
) {
// Sass.js already found a file,
// or it's definitely not a remote file,
// or it's a full url,
// let Sass.js to do its job.
done();
} else {
// console.log('request.current', request.current);
// console.log('request.previous', request.previous);

let remotePath = request.current;
if (request.previous.startsWith('/node_modules/')) {
remotePath = resolveModuleId(request.previous.slice(14), './' + request.current);
}
// console.log('remotePath', remotePath);

this.fetchRemoteFile(remotePath).then(
({path, contents}) => {
// console.log("got remote " + path + ' ' + contents);
done({
path: '/node_modules/' + path.slice(23),
content: contents
});
},
err => {
// console.log(err);
done({error: err.message});
}
);
}
});

return Sass;
});
}

return this._promise;
Expand All @@ -30,33 +117,39 @@ export class SassTranspiler {
async transpile(file, files) {
const {filename} = file;
if (!this.match(file)) throw new Error('Cannot use SassTranspiler for file: ' + filename);
if (path.basename(filename).startsWith('_')) {

const parsed = parse(filename);
if (_.last(parsed.parts).startsWith('_')) {
// ignore sass partial
return;
}

const Sass = await this._lazyLoad();

const ext = path.extname(filename);

const cssFiles = {};
_.each(files, f => {
const ext = path.extname(f.filename);
if (EXTS.indexOf(ext) !== -1 || ext === '.css') {
const e = ext(f.filename);
if (EXTS.indexOf(e) !== -1 || e === '.css') {
cssFiles[f.filename] = f.content;
}
});

const newFilename = filename.slice(0, -ext.length) + '.css';
const newFilename = filename.slice(0, -parsed.ext.length) + '.css';
if (file.content.match(/^\s*$/)) {
return {filename: newFilename, content: ''};
}

return new Promise((resolve, reject) => {
Sass.writeFile(cssFiles, () => {
Sass.compileFile(
filename,
Sass.compile(
file.content,
{
indentedSyntax: parsed.ext === '.sass',
sourceMapRoot: '/',
inputPath: '/sass/' + filename
},
result => {
// console.log('result', result);
Sass.removeFile(Object.keys(cssFiles), () => {
if (result.status === 0) {
const {text, map} = result;
Expand Down
Loading

0 comments on commit 2e9a132

Please sign in to comment.