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

Template Support #137

Merged
merged 24 commits into from
Aug 20, 2017
Prev Previous commit
Next Next commit
Full Sass HMR support
  • Loading branch information
Ives van Hoorne committed Aug 19, 2017
commit 0f77b62c6b35f1e222fd868f59aa5c7cdffb24b3
3 changes: 3 additions & 0 deletions src/app/components/sandbox/CodeEditor/Monaco.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
if (kind) {
if (kind[1] === 'css') {
return 'css';
}
if (kind[1] === 'scss') {
return 'scss';
} else if (kind[1] === 'html') {
return 'html';
} else if (kind[1] === 'md') {
Expand Down
2 changes: 1 addition & 1 deletion src/common/sandbox/resolve-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default (
modules: Array<Module>,
directories: Array<Directory>,
startdirectoryShortid: ?string = undefined,
) => {
): Module => {
if (!path) return null;
// Split path
const splitPath = path.replace(/^.\//, '').split('/');
Expand Down
16 changes: 4 additions & 12 deletions src/sandbox/eval2/Manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,14 @@ export default class Manager {
resolveTranspiledModule(
path: string,
startdirectoryShortid: ?string,
): ?TranspiledModule {
): TranspiledModule {
const module = resolveModule(
path,
this.getModules(),
this.getDirectories(),
startdirectoryShortid,
);

if (!module) return null;

return this.getTranspiledModule(module);
}

Expand Down Expand Up @@ -174,10 +172,8 @@ export default class Manager {
this.modules = modules;
this.directories = directories;

const addedTranspiledModules = addedModules.map(m => this.addModule(m));
const updatedTranspiledModules = updatedModules.map(m =>
this.getTranspiledModule(m).update(m),
);
addedModules.forEach(m => this.addModule(m));
updatedModules.forEach(m => this.getTranspiledModule(m).update(m));

deletedModules.forEach(m => {
const transpiledModule = this.getTranspiledModule(m);
Expand All @@ -186,10 +182,6 @@ export default class Manager {
delete this.transpiledModules[m.id];
});

return Promise.all(
[...addedTranspiledModules, ...updatedTranspiledModules].map(m =>
m.transpile(this),
),
);
return this.transpileAllModules();
}
}
46 changes: 29 additions & 17 deletions src/sandbox/eval2/TranspiledModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,14 @@ export type LoaderContext = {
path: string,
getModules: () => Array<Module>,
resolvePath: (module: Module) => string,
addDependency: (depPath: string) => void,
_module: TranspiledModule, // eslint-disable-line no-use-before-define
};

class Compilation {
dependencies: Set<TranspiledModule>; // eslint-disable-line no-use-before-define
initiators: Set<TranspiledModule>; // eslint-disable-line no-use-before-define
exports: any;

constructor() {
this.dependencies = new Set();
this.initiators = new Set();
this.exports = null;
}
}
Expand All @@ -73,13 +70,17 @@ export default class TranspiledModule {
*/
emittedAssets: Array<ModuleSource>;
compilation: ?Compilation;
initiators: Set<TranspiledModule>; // eslint-disable-line no-use-before-define
dependencies: Set<TranspiledModule>; // eslint-disable-line no-use-before-define

constructor(module: Module) {
this.module = module;
this.errors = [];
this.warnings = [];
this.cacheable = true;
this.childModules = [];
this.dependencies = new Set();
this.initiators = new Set();
}

dispose() {
Expand All @@ -100,12 +101,12 @@ export default class TranspiledModule {
}

resetCompilation() {
if (this.compilation != null) {
this.compilation.initiators.forEach(dep => {
dep.resetCompilation();
});
this.compilation = null;
}
this.initiators.forEach(dep => {
dep.resetCompilation();
});
this.compilation = null;
this.dependencies = new Set();
this.initiators = new Set();
}

update(module: Module): TranspiledModule {
Expand Down Expand Up @@ -154,6 +155,11 @@ export default class TranspiledModule {
emitFile: (name: string, content: string, sourceMap: SourceMap) => {
this.assets[name] = this.createSourceForAsset(name, content, sourceMap);
},
addDependency: (depPath: string) => {
const tModule = manager.resolveTranspiledModule(depPath);
this.dependencies.add(tModule);
tModule.initiators.add(this);
},
getModules: (): Array<Module> => manager.getModules(),
resolvePath: (module: Module) =>
getModulePath(
Expand All @@ -176,8 +182,13 @@ export default class TranspiledModule {
}

async transpile(manager: Manager) {
// For now we only support one transpiler per module
const transpilers = manager.preset.getTranspilers(this.module);
const cacheable = transpilers.every(t => t.cacheable);

if (this.source && cacheable) {
return this;
}

const loaderContext = this.getLoaderContext(manager);

let code = this.module.code || '';
Expand All @@ -198,6 +209,10 @@ export default class TranspiledModule {
}
}

// Add the source of the file by default, this is important for source mapping
// errors back to their origin
code = `${code}\n//# sourceURL=${loaderContext.path}`;

this.source = new ModuleSource(this.module.title, code, finalSourceMap);
return this;
}
Expand All @@ -216,7 +231,7 @@ export default class TranspiledModule {
const module = this.module;
const transpiledModule = this;
const compilation = new Compilation();
compilation.initiators = new Set(parentModules);
this.initiators = new Set(parentModules);
try {
function require(path: string) {
// eslint-disable-line no-unused-vars
Expand All @@ -237,15 +252,12 @@ export default class TranspiledModule {
throw new Error(`${module.title} is importing itself`);
}

compilation.dependencies.add(requiredTranspiledModule);
transpiledModule.dependencies.add(requiredTranspiledModule);

// Check if this module has been evaluated before, if so return the exports
// of that compilation
const cache = requiredTranspiledModule.compilation;

if (cache != null) {
cache.initiators.add(transpiledModule);
}
requiredTranspiledModule.initiators.add(transpiledModule);

// This is a cyclic dependency, we should return undefined for first
// execution according to ES module spec
Expand Down
12 changes: 4 additions & 8 deletions src/sandbox/eval2/transpilers/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import type { SourceMap } from './utils/get-source-map';
import TranspiledModule, { type LoaderContext } from '../TranspiledModule';
import { type LoaderContext } from '../TranspiledModule';

type TranspilerResult = {
transpiledCode: string,
Expand All @@ -13,9 +13,11 @@ export default class Transpiler {
cachedResults: {
[id: string]: TranspilerResult,
};
cacheable: boolean;

constructor() {
this.cachedResults = {};
this.cacheable = true;
}

/* eslint-disable */
Expand All @@ -35,17 +37,11 @@ export default class Transpiler {
code: string,
loaderContext: LoaderContext,
): Promise<TranspilerResult> {
if (this.cachedResults[code]) {
if (this.cacheable && this.cachedResults[code]) {
return Promise.resolve(this.cachedResults[code]);
}

return this.doTranspilation(code, loaderContext).then(result => {
// Add the source of the file by default, this is important for source mapping
// errors back to their origin

// eslint-disable-next-line no-param-reassign
result.transpiledCode = `${result.transpiledCode}\n//# sourceURL=${loaderContext.path}`;

this.cachedResults[code] = result;
return result;
});
Expand Down
12 changes: 9 additions & 3 deletions src/sandbox/eval2/transpilers/sass/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ class SassTranspiler extends WorkerTranspiler {
worker: Worker;

constructor() {
super(SassWorker, 2);
super(SassWorker, 1);

this.cacheable = false;
}

doTranspilation(code: string, loaderContext: LoaderContext) {
const modules = loaderContext.getModules();

const sassModules = modules.filter(m => m.title.endsWith('scss'));
const sassModules = modules.filter(m => /\.s?[a|c]ss$/.test(m.title));
const files = sassModules.reduce(
(interMediateFiles, module) => ({
...interMediateFiles,
Expand All @@ -38,7 +40,11 @@ class SassTranspiler extends WorkerTranspiler {
return reject(err);
}

return resolve(data);
if (data.type === 'add-dependency') {
loaderContext.addDependency(data.path);
} else {
return resolve(data);
}
},
);
});
Expand Down
48 changes: 32 additions & 16 deletions src/sandbox/eval2/transpilers/sass/sass-worker.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { buildWorkerError } from '../utils/worker-error-handler';

self.importScripts([
'https://cdnjs.cloudflare.com/ajax/libs/sass.js/0.10.6/sass.sync.min.js',
]);
Expand All @@ -13,25 +11,43 @@ declare var Sass: {
registerPlugin: (name: string, plugin: Function) => void,
};

Sass.options({
sourceMapEmbed: true,
});

self.addEventListener('message', event => {
const { files, path } = event.data;

Sass.writeFile(files, () => {
try {
const code = Sass.compileFile(path);
self.postMessage({
type: 'compiled',
transpiledCode: code,
});
} catch (e) {
// register a custom importer callback
Sass.importer((request, done) => {
// We use this to mark dependencies of this file
if (request.path) {
self.postMessage({
type: 'error',
error: buildWorkerError(e),
type: 'add-dependency',
path: request.path.replace('/sass/', ''),
});
}
done();
});

Sass.clearFiles(() => {
Sass.options({ sourceMapEmbed: true }, () => {
Sass.writeFile(files, () => {
Sass.compileFile(path, result => {
console.log(result);
if (result.status === 0) {
self.postMessage({
type: 'compiled',
transpiledCode: result.text,
});
} else {
self.postMessage({
type: 'error',
error: {
name: 'CompileError',
message: result.formatted,
fileName: result.file.replace('/sass/', ''),
},
});
}
});
});
});
});
});
9 changes: 6 additions & 3 deletions src/sandbox/eval2/transpilers/worker-transpiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,14 @@ export default class WorkerTranspiler extends Transpiler {
}

callback(null, data);
}

this.idleWorkers.push(worker);
// Means the transpile task has been completed
if (data.type === 'compiled' || data.type === 'error') {
this.idleWorkers.push(worker);

this.executeRemainingTasks();
this.executeRemainingTasks();
}
}
};
worker.postMessage(message);
}
Expand Down