Skip to content

Commit 179b1f7

Browse files
authored
Template Support (codesandbox#137)
* Intermediate * Set up base template system * Tweaks * Mess * Working version * Feature parity * Sass * Full Sass HMR support * Stylus support * Vue supportg! * Fix caching bug * Vue scoped support * CSS Modules support * Preset support * Fix css cache * Less support * Intermediate * Generation * Template settings * Fix tests * exclude tests in templates * Add less syntax * Rename codesandbox to codesandbox-api * Add codesandbox api dependency
1 parent 4c5e51a commit 179b1f7

File tree

128 files changed

+20884
-1127
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+20884
-1127
lines changed

.eslintrc

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
{
2-
"extends": [
3-
"airbnb",
4-
"prettier",
5-
"prettier/react",
6-
"prettier/flowtype"
7-
],
2+
"extends": ["airbnb", "prettier", "prettier/react", "prettier/flowtype"],
83
"parser": "babel-eslint",
94
"env": {
105
"browser": true,
@@ -21,7 +16,8 @@
2116
"react/sort-comp": 0,
2217
"import/no-extraneous-dependencies": 0,
2318
"arrow-parens": 0,
24-
"import/prefer-default-export": 0
19+
"import/prefer-default-export": 0,
20+
"class-methods-use-this": 0
2521
},
2622
"settings": {
2723
"import/resolver": {

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# See http://help.github.com/ignore-files/ for more about ignoring files.
22

33
# dependencies
4-
node_modules
4+
/node_modules/**
55

66
# testing
77
coverage

config/env.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ const REACT_APP = /^REACT_APP_/i;
55
const NODE_ENV = JSON.stringify(process.env.NODE_ENV || 'development');
66
const LOCAL_SERVER = !!JSON.stringify(process.env.LOCAL_SERVER);
77

8+
const getHost = () => {
9+
if (LOCAL_SERVER) {
10+
return 'http://localhost:3000';
11+
}
12+
13+
return process.env.NODE_ENV === 'development'
14+
? '*'
15+
: 'https://codesandbox.io';
16+
};
17+
818
module.exports = Object.keys(process.env)
919
.filter(key => REACT_APP.test(key))
1020
.reduce(
@@ -14,6 +24,7 @@ module.exports = Object.keys(process.env)
1424
},
1525
{
1626
'process.env.NODE_ENV': NODE_ENV,
17-
'process.env.LOCAL_SERVER': !!LOCAL_SERVER
18-
}
27+
'process.env.CODESANDBOX_HOST': JSON.stringify(getHost()),
28+
'process.env.LOCAL_SERVER': !!LOCAL_SERVER,
29+
},
1930
);

config/webpack.config.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,33 @@ const config = {
7575

7676
module: {
7777
rules: [
78+
{
79+
test: /create-zip\/.*\/files\/.*\.ico$/,
80+
loader: 'base64-loader',
81+
},
82+
{
83+
test: /create-zip\/.*\/files\/.*$/,
84+
exclude: [/create-zip\/.*\/files\/.*\.ico$/],
85+
loader: 'raw-loader',
86+
},
7887
{
7988
test: /\.js$/,
8089
include: paths.src,
81-
exclude: [/eslint\.4\.1\.0\.min\.js$/, /typescriptServices\.js$/],
90+
exclude: [
91+
/eslint\.4\.1\.0\.min\.js$/,
92+
/typescriptServices\.js$/,
93+
// Don't do the node modules of the codesandbox module itself
94+
/codesandbox\/node_modules/,
95+
/create-zip\/.*\/files\/.*$/,
96+
],
8297
loader: 'happypack/loader',
8398
},
8499
// JSON is not enabled by default in Webpack but both Node and Browserify
85100
// allow it implicitly so we also enable it.
86101
{
87102
test: /\.json$/,
88103
loader: 'json-loader',
104+
exclude: [/create-zip\/.*\/files\/.*$/],
89105
},
90106
// "postcss" loader applies autoprefixer to our CSS.
91107
// "css" loader resolves paths in CSS and adds assets as dependencies.
@@ -95,18 +111,20 @@ const config = {
95111
{
96112
test: /\.css$/,
97113
loaders: ['style-loader', 'css-loader'],
114+
exclude: [/create-zip\/.*\/files\/.*$/],
98115
},
99116
// For importing README.md
100117
{
101118
test: /\.md$/,
102119
loader: 'raw-loader',
120+
exclude: [/create-zip\/.*\/files\/.*$/],
103121
},
104122
// "file" loader makes sure those assets get served by WebpackDevServer.
105123
// When you `import` an asset, you get its (virtual) filename.
106124
// In production, they would get copied to the `build` folder.
107125
{
108126
test: /\.(ico|jpg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
109-
exclude: /\/favicon.ico$/,
127+
exclude: [/\/favicon.ico$/, /create-zip\/.*\/files\/.*$/],
110128
loader: 'file-loader',
111129
options: {
112130
name: 'static/media/[name].[hash:8].[ext]',
@@ -116,6 +134,7 @@ const config = {
116134
{
117135
test: /\/favicon.ico$/,
118136
include: [paths.src],
137+
exclude: [/create-zip\/.*\/files\/.*$/],
119138
loader: 'file-loader',
120139
options: {
121140
name: 'favicon.ico?[hash:8]',
@@ -126,6 +145,7 @@ const config = {
126145
{
127146
test: /\.(mp4|webm)(\?.*)?$/,
128147
loader: 'url-loader',
148+
exclude: [/create-zip\/.*\/files\/.*$/],
129149
options: {
130150
limit: 10000,
131151
name: 'static/media/[name].[hash:8].[ext]',
@@ -136,6 +156,7 @@ const config = {
136156
{
137157
test: /\.html$/,
138158
loader: 'html-loader',
159+
exclude: [/create-zip\/.*\/files\/.*$/],
139160
options: {
140161
attrs: ['link:href'],
141162
},
@@ -241,6 +262,10 @@ const config = {
241262
: 'node_modules/monaco-editor/min/vs',
242263
to: 'public/vs',
243264
},
265+
{
266+
from: 'static',
267+
to: 'static',
268+
},
244269
{
245270
from: 'src/homepage/static',
246271
to: 'static',
@@ -460,6 +485,15 @@ if (__PROD__) {
460485
},
461486
},
462487
},
488+
{
489+
urlPattern: /cloudflare\.com/,
490+
handler: 'cacheFirst',
491+
options: {
492+
cache: {
493+
name: 'cloudflare-cache',
494+
},
495+
},
496+
},
463497
],
464498
}),
465499
// Moment.js is an extremely popular library that bundles large locale files

jsconfig.json

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
{
2-
"compilerOptions": {
3-
"module": "commonjs",
4-
"target": "ES6"
5-
},
6-
"files": [
7-
"src/"
8-
],
9-
"exclude": [
10-
"node_modules"
11-
]
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"target": "ES6",
5+
"paths": {
6+
"sandbox/*": "src/sandbox/*",
7+
"app/*": "src/app/*",
8+
"common/*": "src/common/*",
9+
"codesandbox/*": "src/codesandbox/*"
10+
}
11+
},
12+
"files": ["src/**/*.js"],
13+
"exclude": ["node_modules/**/*.*"]
1214
}

package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
"dependencies": {
8181
"@emmetio/codemirror-plugin": "^0.3.5",
8282
"axios": "^0.16.2",
83+
"babel-plugin-detective": "^2.0.0",
8384
"babel-plugin-dynamic-import-node": "^1.0.2",
8485
"babel-plugin-system-import": "^1.1.5",
8586
"babel-plugin-system-import-transformer": "3.1.0",
@@ -91,7 +92,9 @@
9192
"babel-standalone": "^6.25.0",
9293
"base64-loader": "^1.0.0",
9394
"codemirror": "^5.27.4",
95+
"codesandbox-api": "^0.0.3",
9496
"color": "^0.11.4",
97+
"css-modules-loader-core": "^1.1.0",
9598
"cssnano": "^3.10.0",
9699
"custom-prettier-codesandbox": "CompuIves/custom-prettier-codesandbox",
97100
"debug": "^2.6.8",
@@ -103,6 +106,7 @@
103106
"gulp-filter": "^5.0.0",
104107
"gulp-postcss": "^6.4.0",
105108
"gulp-rev": "^7.1.2",
109+
"hash-sum": "^1.0.2",
106110
"humps": "CompuIves/humps",
107111
"jszip": "^3.1.3",
108112
"lodash": "^4.17.2",
@@ -111,6 +115,8 @@
111115
"monaco-editor": "CompuIves/codesandbox-monaco-editor",
112116
"normalize.css": "^5.0.0",
113117
"normalizr": "^3.2.3",
118+
"postcss": "^6.0.9",
119+
"postcss-selector-parser": "^2.2.3",
114120
"qs": "^6.5.0",
115121
"rc-slider": "^8.2.0",
116122
"react": "^15.6.1",
@@ -138,7 +144,10 @@
138144
"store": "^2.0.12",
139145
"styled-components": "^2.1.1",
140146
"svg-react-loader": "^0.4.4",
141-
"tern": "^0.21.0"
147+
"tern": "^0.21.0",
148+
"vue": "^2.4.2",
149+
"vue-template-compiler": "^2.4.2",
150+
"vue-template-es2015-compiler": "^1.5.3"
142151
},
143152
"scripts": {
144153
"start": "cross-env LOCAL_SERVER=1 node scripts/start.js",
@@ -167,6 +176,7 @@
167176
"jest": {
168177
"rootDir": "src",
169178
"moduleDirectories": ["node_modules", "src"],
179+
"testPathIgnorePatterns": ["/node_modules/", "/create-zip\\/.*\\/files/"],
170180
"moduleNameMapper": {
171181
"\\.css$": "<rootDir>/__mocks__/styleMock.js",
172182
"\\.html$": "<rootDir>/__mocks__/styleMock.js"

src/app/components/Switch.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import React from 'react';
22
import styled from 'styled-components';
33

44
const getColor = ({ right, offMode, secondary, theme }) => {
5-
if (right) return secondary ? theme.secondary : theme.primary;
5+
if (right) {
6+
return secondary ? theme.templateColor || theme.secondary : theme.primary;
7+
}
68
if (offMode) return `rgba(0, 0, 0, 0.3)`;
7-
return secondary ? theme.primary : theme.secondary;
9+
return secondary ? theme.primary : theme.templateColor || theme.secondary;
810
};
911

1012
const Container = styled.div`

src/app/components/sandbox/CodeEditor/Monaco.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
160160
fuzzySearchEnabled: false,
161161
};
162162

163-
syntaxWorker: ServiceWorker;
164-
lintWorker: ServiceWorker;
165-
typingsFetcherWorker: ServiceWorker;
163+
syntaxWorker: Worker;
164+
lintWorker: Worker;
165+
typingsFetcherWorker: Worker;
166166
sizeProbeInterval: number;
167167

168168
setupWorkers = () => {
@@ -412,25 +412,37 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
412412
}
413413

414414
getMode = (title: string) => {
415-
if (title == null) return 'typescript';
415+
if (title == null) return 'javascript';
416416

417417
const kind = title.match(/\.([^.]*)$/);
418418

419419
if (kind) {
420420
if (kind[1] === 'css') {
421421
return 'css';
422+
}
423+
if (kind[1] === 'scss') {
424+
return 'scss';
422425
} else if (kind[1] === 'html') {
423426
return 'html';
427+
} else if (kind[1] === 'vue') {
428+
return 'html';
429+
} else if (kind[1] === 'less') {
430+
return 'less';
424431
} else if (kind[1] === 'md') {
425432
return 'markdown';
433+
} else if (/jsx?$/.test(kind[1])) {
434+
return 'javascript';
435+
} else if (/tsx?$/.test(kind[1])) {
436+
return 'typescript';
426437
}
427438
}
428439

429440
return 'typescript';
430441
};
431442

432443
syntaxHighlight = (code: string, title: string, version: string) => {
433-
if (this.getMode(title) === 'typescript') {
444+
const mode = this.getMode(title);
445+
if (mode === 'typescript' || mode === 'javascript') {
434446
this.syntaxWorker.postMessage({
435447
code,
436448
title,
@@ -440,7 +452,7 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
440452
};
441453

442454
lint = (code: string, title: string, version: string) => {
443-
if (this.getMode(title) === 'typescript') {
455+
if (this.getMode(title) === 'javascript') {
444456
this.lintWorker.postMessage({
445457
code,
446458
title,
@@ -610,9 +622,10 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
610622
// Remove the first slash, as this will otherwise create errors in monaco
611623
const path = getModulePath(modules, directories, module.id);
612624

625+
const mode = this.getMode(module.title);
613626
const model = this.monaco.editor.createModel(
614627
module.code,
615-
this.getMode(module.title),
628+
mode === 'javascript' ? 'typescript' : mode,
616629
new this.monaco.Uri().with({ path, scheme: 'file' }),
617630
);
618631

@@ -705,12 +718,12 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
705718
const code = this.getCode();
706719
const mode = this.getMode(title);
707720

708-
if (mode === 'typescript' || mode === 'css') {
721+
if (mode === 'javascript' || mode === 'css') {
709722
try {
710723
const prettify = await import('app/utils/codemirror/prettify');
711724
const newCode = await prettify.default(
712725
code,
713-
mode === 'typescript' ? 'jsx' : mode,
726+
mode === 'javascript' ? 'jsx' : mode,
714727
false, // Force false for eslint, since we would otherwise include 2 eslint bundles
715728
preferences.prettierConfig,
716729
);

src/app/components/sandbox/CodeEditor/monaco/workers/fetch-dependency-typings.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ const transformFiles = dir =>
112112
: {};
113113

114114
const getFileMetaData = (depUrl, depPath) =>
115-
doFetch(`${depUrl}/${path.dirname(depPath)}?json`)
115+
doFetch(`${depUrl}/${path.dirname(depPath)}?meta`)
116116
.then(response => JSON.parse(response))
117117
.then(transformFiles);
118118

src/app/components/sandbox/ModeIcons.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React from 'react';
22
import styled, { keyframes } from 'styled-components';
3-
import theme from 'common/theme';
43

54
import Tooltip from 'app/components/Tooltip';
65

@@ -82,11 +81,11 @@ const Icon = styled.div`
8281
`;
8382

8483
export const EditorIcon = styled(Icon)`
85-
background-color: ${() => theme.secondary};
84+
background-color: ${({ theme }) => theme.templateColor || theme.secondary};
8685
`;
8786

8887
export const PreviewIcon = styled(Icon)`
89-
background-color: ${() => theme.primary};
88+
background-color: ${({ theme }) => theme.primary};
9089
`;
9190

9291
type Props = {

0 commit comments

Comments
 (0)