Skip to content

Commit

Permalink
Merge pull request #308 from connect-foundation/coconut
Browse files Browse the repository at this point in the history
Coconut worker build 기능 추가
  • Loading branch information
YukJiSoo authored Dec 19, 2019
2 parents 26611b8 + d4ebf44 commit 336ed02
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 55 deletions.
9 changes: 5 additions & 4 deletions coconut/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,19 @@
"react": "^16.12.0",
"react-app-rewired": "^2.1.5",
"react-dom": "^16.12.0",
"react-helmet": "^5.2.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.3.0",
"styled-components": "^4.4.1",
"worker-loader": "^2.0.0"
},
"devDependencies": {
"prettier": "^1.19.1",
"@commitlint/cli": "^8.2.0",
"@commitlint/config-conventional": "^8.2.0",
"eslint-config-prettier": "^6.5.0",
"lint-staged": "^9.4.2",
"husky": "^3.0.9",
"@commitlint/cli": "^8.2.0",
"@commitlint/config-conventional": "^8.2.0"
"lint-staged": "^9.4.2",
"prettier": "^1.19.1"
},
"scripts": {
"start": "NODE_PATH=src react-app-rewired start",
Expand Down
49 changes: 49 additions & 0 deletions coconut/src/bundler/codeTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const scriptCodeTemplate = (code, newPath, parentPath) => /*javascript*/ `
exports['${newPath}'] = (() => {
window.pathStack = [];
pathStack.push('${parentPath}');
let exports = {};
let module = { exports: {} };
try {
${code}
return Object.keys(exports).length ? exports : module.exports;
} catch (e) {
const ignoreErrorList = [
'Cannot redefine property',
'Cannot read property',
'Cannot set property default of #<Object> which has only a getter'
];
const errorType = e.message;
const isExistInIgnoreList = ignoreErrorList.some(ignoreError =>
errorType.startsWith(ignoreError)
);
if(!isExistInIgnoreList) throw e;
}
})()`;

const executeCodeTemplate = code => /*javascript*/ `
(() => {
let exports = {};
try {
${code}
return Object.keys(exports).length ? exports : module.exports;
} catch (e) {
const ignoreErrorList = [
'Cannot redefine property',
'Cannot read property',
'Cannot set property default of #<Object> which has only a getter',
'document is not defined'
];
const errorType = e.message;
const isExistInIgnoreList = ignoreErrorList.some(ignoreError =>
errorType.startsWith(ignoreError)
);
if(!isExistInIgnoreList) throw e;
}
})()`;

export { scriptCodeTemplate, executeCodeTemplate };
4 changes: 3 additions & 1 deletion coconut/src/bundler/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import { pathStack } from './global';
function transformCode(code) {
try {
const result = babel.transform(code, {
presets: [presetEnv, presetReact]
presets: [presetEnv, presetReact],
compact: true,
minified: true
});
return {
state: true,
Expand Down
4 changes: 2 additions & 2 deletions coconut/src/bundler/pathParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ function pathParser(param) {
if (extension[extension.length - 1] === 'js') {
return [param, path.dirname(param)];
}
if (window.fileSystem[`${param}.js`]) {
if (self.fileSystem[`${param}.js`]) {
return [`${param}.js`, path.dirname(param)];
}
if (window.fileSystem[`${param}/index.js`]) {
if (self.fileSystem[`${param}/index.js`]) {
return [`${param}/index.js`, param];
}

Expand Down
28 changes: 28 additions & 0 deletions coconut/src/bundler/pathParserInMain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import path from 'path';

const DEPENDENCY_PATH = '/node_modules/';

function pathParser(param) {
const moduleName = param;

if (param[0] !== '.' && param[0] !== '/') {
param = `${DEPENDENCY_PATH}${param}`;
}

param = path.resolve(window.pathStack[window.pathStack.length - 1], param);
const extension = param.split('.');

if (extension[extension.length - 1] === 'js') {
return [param, path.dirname(param)];
}
if (window.fileSystem[`${param}.js`]) {
return [`${param}.js`, path.dirname(param)];
}
if (window.fileSystem[`${param}/index.js`]) {
return [`${param}/index.js`, param];
}

throw Error(`Module not found: '${moduleName}'`);
}

export { pathParser };
32 changes: 8 additions & 24 deletions coconut/src/bundler/require.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,17 @@
import { pathStack } from './global';
import { pathParser } from './pathParser';
import { transformCode } from './core';

const executeCodeTemplate = code => /*javascript*/ `
(() => {
let exports = {};
try {
${code}
return Object.keys(exports).length ? exports : module.exports;
} catch (e) {
const ignoreErrorList = [
'Cannot redefine property',
'Cannot read property',
'Cannot set property default of #<Object> which has only a getter'
];
const errorType = e.message;
const isExistInIgnoreList = ignoreErrorList.some(ignoreError =>
errorType.startsWith(ignoreError)
);
if(!isExistInIgnoreList) throw e;
}
})()`;
import { scriptCodeTemplate, executeCodeTemplate } from './codeTemplate';

function require(path) {
if (path === '.' || path === './')
throw Error('Recursive path parsing error');
const [newPath, newPathParent] = pathParser(path);

if (window.exports[newPath]) return window.exports[newPath];
if (self.exports[newPath]) return self.exports[newPath];

pathStack.push(newPathParent);
const code = transformCode(window.fileSystem[newPath].contents).value;
const code = transformCode(self.fileSystem[newPath].contents).value;

let result = null;
let stackLength = 0;
Expand All @@ -43,8 +23,12 @@ function require(path) {
result = eval(executeCodeTemplate(code));
}

window.exports[newPath] = result;
self.bundledCode = `
${self.bundledCode}\n${scriptCodeTemplate(code, newPath, newPathParent)}`;

self.exports[newPath] = result;
pathStack.pop();

return result;
}

Expand Down
32 changes: 32 additions & 0 deletions coconut/src/bundler/requireInMain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { pathStack } from './global';
import { pathParser } from './pathParserInMain';
import { transformCode } from './core';
import { executeCodeTemplate } from './codeTemplate';

function requireInMain(path) {
if (path === '.' || path === './')
throw Error('Recursive path parsing error');
const [newPath, newPathParent] = pathParser(path);

if (window.exports[newPath]) return window.exports[newPath];

pathStack.push(newPathParent);
const code = transformCode(window.fileSystem[newPath].contents).value;

let result = null;
let stackLength = 0;
try {
stackLength = pathStack.length;
result = eval(executeCodeTemplate(code));
} catch (error) {
while (stackLength < pathStack.length) pathStack.pop();
result = eval(executeCodeTemplate(code));
}

window.exports[newPath] = result;
pathStack.pop();

return result;
}

export { requireInMain };
20 changes: 17 additions & 3 deletions coconut/src/components/Coconut/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useReducer, useCallback } from 'react';
import { useParams } from 'react-router-dom';
import { Helmet } from 'react-helmet';
import * as Styled from './style';

import CoconutSpinner from 'components/CoconutSpinner';
Expand All @@ -15,6 +16,9 @@ import {

import ProjectReducer from 'reducers/ProjectReducer';

import BuildWorker from 'worker/build.worker.js';
import { requireInMain } from 'bundler/requireInMain';

const PROJECT_IDBNAME = 'Coconut';
const DEPENDENCY_IDBNAME = 'Dependency';

Expand Down Expand Up @@ -57,6 +61,9 @@ const buildProjectFailState = errorPhrase => ({
function Coconut() {
const { projectId } = useParams();

const [worker] = useState(new BuildWorker());
const [innerCode, setInnerCode] = useState('');

const [buildState, setBuildState] = useState(initilaBuildState);
const [project, dispatchProject] = useReducer(ProjectReducer, undefined);

Expand Down Expand Up @@ -85,6 +92,7 @@ function Coconut() {
return;
}

window.require = requireInMain;
return () => closeConnections();
}, [iDBConnectionState, closeConnections]);

Expand Down Expand Up @@ -112,19 +120,22 @@ function Coconut() {
return;
}

buildProject(project);
}, [dependencyState, buildProject, project]);
buildProject(project, worker);
}, [dependencyState, buildProject, project, worker]);

const handleBuildProject = useCallback(() => {
if (!buildResult) return;

const { error } = buildResult;
const { error, bundledCode } = buildResult;
const resultBuildState = error
? buildProjectFailState(error)
: successBuildState;
setBuildState(resultBuildState);

sendToCocode({ command: BUILD_END });
if (error) return;

setInnerCode(bundledCode);
}, [buildResult, sendToCocode]);

useEffect(handleConnectToIDB, [iDBConnectionState]);
Expand All @@ -142,6 +153,9 @@ function Coconut() {
</Styled.BuildStateOverlay>
)}
<div id="coconut-root" />
<Helmet>
<script>{innerCode}</script>
</Helmet>
</>
);
}
Expand Down
35 changes: 14 additions & 21 deletions coconut/src/hooks/useBuildProject.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import { useState, useCallback } from 'react';

import * as bundler from 'bundler';

function useBuildProject() {
const [buildResult, setBuildResult] = useState(undefined);

const buildProject = (project, worker) => {
const { files, root } = project;

const rootPath = files[root].path;
fileParser(project, rootPath, root);

worker.postMessage({ fileSystem: window.fileSystem });

worker.onmessage = ({ data: { error, bundledCode } }) => {
if (!error) setBuildResult({ bundledCode });
else setBuildResult({ error });
};
};

const fileParser = useCallback((project, path, id) => {
const { files } = project;

Expand All @@ -21,25 +33,6 @@ function useBuildProject() {
}
}, []);

const buildProject = useCallback(
project => {
const { files, root } = project;

const rootPath = files[root].path;
if (project) fileParser(project, rootPath, root);

try {
bundler.init();
bundler.require('./index.js');

setBuildResult({ error: undefined });
} catch (error) {
setBuildResult({ error: error.stack });
}
},
[fileParser, setBuildResult]
);

return [buildResult, buildProject];
}

Expand Down
25 changes: 25 additions & 0 deletions coconut/src/worker/build.worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as bundler from 'bundler';

self.process = {};
self.process.env = {};
self.process.env.NODE_MODULE = 'development';

self.exports = {};

self.addEventListener('message', ({ data: { fileSystem } }) => {
self.fileSystem = fileSystem;
self.bundledCode = '';

buildProject();
});

function buildProject() {
try {
bundler.init();
bundler.require('./index.js');

self.postMessage({ bundledCode: self.bundledCode });
} catch (error) {
self.postMessage({ error: error.stack });
}
}
Loading

0 comments on commit 336ed02

Please sign in to comment.