diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..39789b11d5e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.js] +indent_style = space +indent_size = 2 +end_of_line = lf diff --git a/packages/app/package.json b/packages/app/package.json index 14d0a6e4a9e..bd9c41e2f0a 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -106,6 +106,7 @@ "babel-plugin-syntax-jsx": "^6.18.0", "babel-plugin-system-import": "^1.1.5", "babel-plugin-system-import-transformer": "3.1.0", + "babel-plugin-transform-cx-jsx": "^17.12.0", "babel-plugin-transform-async-to-generator": "^6.24.1", "babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-remove-strict-mode": "^0.0.2", diff --git a/packages/app/src/app/components/CodeEditor/Monaco/index.js b/packages/app/src/app/components/CodeEditor/Monaco/index.js index 320a7a41e3a..1e79db852b9 100644 --- a/packages/app/src/app/components/CodeEditor/Monaco/index.js +++ b/packages/app/src/app/components/CodeEditor/Monaco/index.js @@ -4,6 +4,7 @@ import { TextOperation } from 'ot'; import { debounce } from 'lodash'; import { getModulePath } from 'common/sandbox/modules'; import { css } from 'glamor'; +import { listen } from 'codesandbox-api'; import getTemplate from 'common/templates'; import type { @@ -134,6 +135,7 @@ class MonacoEditor extends React.Component implements Editor { editor: any; monaco: any; receivingCode: ?boolean = false; + transpilationListener: ?Function; constructor(props: Props) { super(props); @@ -158,6 +160,8 @@ class MonacoEditor extends React.Component implements Editor { this.onSelectionChangedDebounced, 500 ); + + this.transpilationListener = this.setupTranspilationListener(); } shouldComponentUpdate(nextProps: Props) { @@ -191,6 +195,9 @@ class MonacoEditor extends React.Component implements Editor { if (this.typingsFetcherWorker) { this.typingsFetcherWorker.terminate(); } + if (this.transpilationListener) { + this.transpilationListener(); + } clearTimeout(this.sizeProbeInterval); }); @@ -199,6 +206,18 @@ class MonacoEditor extends React.Component implements Editor { } } + setupTranspilationListener() { + return listen(({ type, code, path }) => { + if (type === 'add-extra-lib') { + const dtsPath = `${path}.d.ts`; + this.monaco.languages.typescript.typescriptDefaults._extraLibs[ + `file:///${dtsPath}` + ] = code; + this.commitLibChanges(); + } + }); + } + configureEditor = async (editor: any, monaco: any) => { this.editor = editor; this.monaco = monaco; diff --git a/packages/app/src/app/components/NewSandbox/index.js b/packages/app/src/app/components/NewSandbox/index.js index 67afb62a3ec..b2439c8c936 100644 --- a/packages/app/src/app/components/NewSandbox/index.js +++ b/packages/app/src/app/components/NewSandbox/index.js @@ -10,7 +10,9 @@ import { newPreactSandboxUrl, newVueSandboxUrl, newAngularSandboxUrl, + newDojoSandboxUrl, newSvelteSandboxUrl, + newCxJSSandboxUrl, importFromGitHubUrl, uploadFromCliUrl, } from 'common/utils/url-generator'; @@ -21,6 +23,8 @@ import ParcelIcon from 'common/components/logos/Parcel'; import VueIcon from 'common/components/logos/Vue'; import SvelteIcon from 'common/components/logos/Svelte'; import AngularIcon from 'common/components/logos/Angular'; +import CxJSIcon from 'common/components/logos/CxJS'; +import DojoIcon from 'common/components/logos/Dojo'; import { Container, @@ -53,7 +57,6 @@ function NewSandbox({ signals }) { href={parcelSandboxUrl()} onClick={() => signals.modalClosed()} /> - signals.modalClosed()} /> - signals.modalClosed()} /> - signals.modalClosed()} /> + signals.closeModal()} + /> + signals.closeModal()} + /> /\.jsx?$/.test(module.path), [ + { + transpiler: babelTranspiler, + options: { + dynamicCSSModules: true, + }, + }, + ]); + + cxjsPreset.registerTranspiler(module => /\.tsx?$/.test(module.path), [ + { transpiler: tsTranspiler }, + ]); + + cxjsPreset.registerTranspiler(module => /\.css$/.test(module.path), [ + { transpiler: stylesTranspiler }, + ]); + + cxjsPreset.registerTranspiler(module => /\.json$/.test(module.path), [ + { transpiler: jsonTranspiler }, + ]); + + const sassWithConfig = { + transpiler: sassTranspiler, + options: {}, + }; + + const lessWithConfig = { + transpiler: lessTranspiler, + options: {}, + }; + + const stylusWithConfig = { + transpiler: stylusTranspiler, + options: {}, + }; + const styles = { + css: [], + scss: [sassWithConfig], + sass: [sassWithConfig], + less: [lessWithConfig], + styl: [stylusWithConfig], + }; + + /** + * Registers transpilers for all different combinations + * + * @returns + */ + function registerStyleTranspilers() { + return Object.keys(styles).forEach(type => { + cxjsPreset.registerTranspiler( + module => new RegExp(`\\.${type}`).test(module.path), + [...styles[type], { transpiler: stylesTranspiler }] + ); + }); + } + + registerStyleTranspilers(); + + cxjsPreset.registerTranspiler(() => true, [{ transpiler: rawTranspiler }]); + + return cxjsPreset; +} diff --git a/packages/app/src/sandbox/eval/presets/dojo/index.js b/packages/app/src/sandbox/eval/presets/dojo/index.js new file mode 100644 index 00000000000..d382099e453 --- /dev/null +++ b/packages/app/src/sandbox/eval/presets/dojo/index.js @@ -0,0 +1,60 @@ +import { join, absolute } from 'common/utils/path'; +import Preset from '../'; + +import typescriptTranspiler from '../../transpilers/typescript'; +import rawTranspiler from '../../transpilers/raw'; +import jsonTranspiler from '../../transpilers/json'; +import stylesTranspiler from '../../transpilers/style'; +import babelTranspiler from '../../transpilers/babel'; + +export default function initialize() { + const preset = new Preset( + '@dojo/cli-create-app', + ['ts', 'tsx', 'js', 'json'], + {}, + { + setup: async manager => { + const stylesPath = absolute(join('src', 'main.css')); + try { + const tModule = await manager.resolveTranspiledModuleAsync( + stylesPath, + '/' + ); + await tModule.transpile(manager); + tModule.setIsEntry(true); + tModule.evaluate(manager); + } catch (e) { + if (e.type === 'module-not-found') { + // Do nothing + } else { + throw e; + } + } + }, + } + ); + + preset.registerTranspiler(module => /\.tsx?$/.test(module.path), [ + { transpiler: typescriptTranspiler }, + ]); + + preset.registerTranspiler(module => /\.jsx?$/.test(module.path), [ + { transpiler: babelTranspiler }, + ]); + + preset.registerTranspiler(module => /\.json$/.test(module.path), [ + { transpiler: jsonTranspiler }, + ]); + + preset.registerTranspiler(module => /\.m\.css$/.test(module.path), [ + { transpiler: stylesTranspiler, options: { module: true } }, + ]); + + preset.registerTranspiler(module => /\.css$/.test(module.path), [ + { transpiler: stylesTranspiler }, + ]); + + preset.registerTranspiler(() => true, [{ transpiler: rawTranspiler }]); + + return preset; +} diff --git a/packages/app/src/sandbox/eval/transpilers/babel/worker/babel-worker.js b/packages/app/src/sandbox/eval/transpilers/babel/worker/babel-worker.js index 73f154263c7..911c816186a 100644 --- a/packages/app/src/sandbox/eval/transpilers/babel/worker/babel-worker.js +++ b/packages/app/src/sandbox/eval/transpilers/babel/worker/babel-worker.js @@ -269,6 +269,14 @@ self.addEventListener('message', async event => { ) { const pragmaticPlugin = await import(/* webpackChunkName: 'babel-plugin-jsx-pragmatic' */ 'babel-plugin-jsx-pragmatic'); Babel.registerPlugin('jsx-pragmatic', pragmaticPlugin); + } + + if ( + flattenedPlugins.indexOf('transform-cx-jsx') > -1 && + Object.keys(Babel.availablePlugins).indexOf('transform-cx-jsx') === -1 + ) { + const cxJsxPlugin = await import(/* webpackChunkName: 'transform-cx-jsx' */ 'babel-plugin-transform-cx-jsx'); + Babel.registerPlugin('transform-cx-jsx', cxJsxPlugin); } } diff --git a/packages/app/src/sandbox/eval/transpilers/style/index.js b/packages/app/src/sandbox/eval/transpilers/style/index.js index ec2a999d69f..75bbf255177 100644 --- a/packages/app/src/sandbox/eval/transpilers/style/index.js +++ b/packages/app/src/sandbox/eval/transpilers/style/index.js @@ -1,4 +1,5 @@ // @flow +import { dispatch } from 'codesandbox-api'; import Transpiler from '../'; import { type LoaderContext } from '../../transpiled-module'; @@ -7,6 +8,13 @@ import getModules from './get-modules'; const getStyleId = id => id + '-css'; // eslint-disable-line +function classesToDefinition(classes): string { + return Object.keys(classes).reduce( + (previous, className) => previous + `export const ${className}: string;\n`, + '' + ); +} + class StyleTranspiler extends Transpiler { constructor() { super('style-loader'); @@ -24,12 +32,18 @@ class StyleTranspiler extends Transpiler { doTranspilation(code: string, loaderContext: LoaderContext) { const id = getStyleId(loaderContext._module.getId()); + const { path } = loaderContext; if (loaderContext.options.module) { return getModules(code, loaderContext).then(({ css, exportTokens }) => { let result = insertCss(id, css); result += `\nmodule.exports=${JSON.stringify(exportTokens)};`; + dispatch({ + type: 'add-extra-lib', + path, + code: classesToDefinition(exportTokens), + }); return Promise.resolve({ transpiledCode: result }); }); } diff --git a/packages/common/components/logos/CxJS.js b/packages/common/components/logos/CxJS.js new file mode 100644 index 00000000000..2ee2f131736 --- /dev/null +++ b/packages/common/components/logos/CxJS.js @@ -0,0 +1,5 @@ +import React from 'react'; + +import cxjs from './cxjs.svg'; + +export default props => cxjs; diff --git a/packages/common/components/logos/Dojo.js b/packages/common/components/logos/Dojo.js new file mode 100644 index 00000000000..e11b82d9f50 --- /dev/null +++ b/packages/common/components/logos/Dojo.js @@ -0,0 +1,267 @@ +import React from 'react'; + +export default ({ + width = 35, + height = 35, + className, +}: { + width: number, + height: number, + className: ?string, +}) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/packages/common/components/logos/cxjs.svg b/packages/common/components/logos/cxjs.svg new file mode 100644 index 00000000000..df3e67bd064 --- /dev/null +++ b/packages/common/components/logos/cxjs.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/common/templates/configuration/babelrc/index.js b/packages/common/templates/configuration/babelrc/index.js index 1ef07c2f012..7bb48634121 100644 --- a/packages/common/templates/configuration/babelrc/index.js +++ b/packages/common/templates/configuration/babelrc/index.js @@ -102,6 +102,36 @@ const config: ConfigurationFile = { return JSON.stringify({ presets, plugins }, null, 2); } + if (template === 'cxjs') { + return JSON.stringify( + { + presets: [ + [ + 'env', + { + targets: { + chrome: 50, + ie: 11, + ff: 30, + edge: 12, + safari: 9, + }, + modules: false, + loose: true, + useBuiltIns: true, + }, + ], + ], + plugins: [ + ['transform-cx-jsx'], + ['transform-react-jsx', { pragma: 'VDOM.createElement' }], + ], + }, + null, + 2 + ); + } + return JSON.stringify({ presets: [], plugins: [] }, null, 2); }, diff --git a/packages/common/templates/cxjs.js b/packages/common/templates/cxjs.js new file mode 100644 index 00000000000..07ee5352163 --- /dev/null +++ b/packages/common/templates/cxjs.js @@ -0,0 +1,32 @@ +// @flow + +import Template from './template'; +import { decorateSelector } from '../theme'; +import configurations from './configuration'; + +class CxJSTemplate extends Template { + getEntries() { + return ['/app/index.js', '/src/index.js', '/index.html']; + } + + getHTMLEntries() { + return ['/app/index.html', '/src/index.html', '/index.html']; + } +} + +export default new CxJSTemplate( + 'cxjs', + 'CxJS', + 'https://cxjs.io/', + 'github/codaxy/cxjs-codesandbox-template', + decorateSelector(() => '#11689f'), + { + showOnHomePage: true, + extraConfigurations: { + '/.babelrc': configurations.babelrc, + '/tsconfig.json': configurations.tsconfig, + }, + externalResourcesEnabled: false, + distDir: 'dist', + } +); diff --git a/packages/common/templates/dojo.js b/packages/common/templates/dojo.js new file mode 100644 index 00000000000..56eaae35656 --- /dev/null +++ b/packages/common/templates/dojo.js @@ -0,0 +1,28 @@ +// @flow + +import Template from './template'; +import { decorateSelector } from '../theme'; + +export class DojoTemplate extends Template { + // eslint-disable-next-line no-unused-vars + getHTMLEntries(configurationFiles: { + [type: string]: Object, + }): Array { + return ['/src/index.html']; + } + + getEntries(configurationFiles: { [type: string]: Object }) { + const entries = super.getEntries(configurationFiles); + entries.push('/src/main.ts'); + return entries; + } +} + +export default new DojoTemplate( + '@dojo/cli-create-app', + 'Dojo 2', + 'https://github.com/dojo/cli-create-app', + 'github/dojo/dojo-codesandbox-template', + decorateSelector(() => '#D3471C'), + { showOnHomePage: true, isTypeScript: true } +); diff --git a/packages/common/templates/icons.js b/packages/common/templates/icons.js index e69fe31e75c..f2298c4f03a 100644 --- a/packages/common/templates/icons.js +++ b/packages/common/templates/icons.js @@ -6,8 +6,20 @@ import Parcel from 'common/components/logos/Parcel'; import Preact from 'common/components/logos/Preact'; import Vue from 'common/components/logos/Vue'; import Svelte from 'common/components/logos/Svelte'; +import Dojo from 'common/components/logos/Dojo'; +import CxJS from 'common/components/logos/CxJS'; -import { react, vue, preact, reactTs, svelte, angular, parcel } from './'; +import { + react, + vue, + preact, + reactTs, + svelte, + angular, + parcel, + dojo, + cxjs, +} from './'; export default function getIcon( theme: @@ -18,6 +30,8 @@ export default function getIcon( | 'create-react-app-typescript' | 'angular-cli' | 'parcel' + | 'dojo' + | 'cxjs' ) { switch (theme) { case react.name: @@ -34,6 +48,10 @@ export default function getIcon( return Angular; case parcel.name: return Parcel; + case dojo.name: + return Dojo; + case cxjs.name: + return CxJS; default: return React; } diff --git a/packages/common/templates/index.js b/packages/common/templates/index.js index fc0d7dc18ad..12e3e136931 100644 --- a/packages/common/templates/index.js +++ b/packages/common/templates/index.js @@ -7,8 +7,21 @@ import react from './react'; import reactTs from './react-ts'; import svelte from './svelte'; import vue from './vue'; +import cxjs from './cxjs'; +import dojo from './dojo'; -export { angular, babel, vue, react, reactTs, preact, svelte, parcel }; +export { + angular, + babel, + vue, + react, + reactTs, + preact, + svelte, + parcel, + dojo, + cxjs, +}; export default function getDefinition( theme: @@ -19,6 +32,8 @@ export default function getDefinition( | 'create-react-app-typescript' | 'angular-cli' | 'parcel' + | 'cxjs' + | '@dojo/cli-create-app' ) { switch (theme) { case react.name: @@ -37,6 +52,10 @@ export default function getDefinition( return parcel; case babel.name: return babel; + case cxjs.name: + return cxjs; + case dojo.name: + return dojo; default: return react; } diff --git a/packages/common/types.js b/packages/common/types.js index f3aef22f36b..887d787f50c 100644 --- a/packages/common/types.js +++ b/packages/common/types.js @@ -140,6 +140,7 @@ export type Sandbox = { | 'create-react-app' | 'create-react-app-typescript' | 'angular-cli' + | '@dojo/cli-create-app' | 'vue-cli' | 'preact-cli' | 'svelte', diff --git a/packages/common/utils/url-generator.js b/packages/common/utils/url-generator.js index f2254b6c09f..61cf8b5c938 100644 --- a/packages/common/utils/url-generator.js +++ b/packages/common/utils/url-generator.js @@ -27,11 +27,15 @@ export const newSandboxWizard = () => `/s`; export const newSandboxUrl = () => `/s/new`; export const parcelSandboxUrl = () => `/s/vanilla`; export const newReactTypeScriptSandboxUrl = () => `/s/react-ts`; +export const newDojoSandboxUrl = () => + `/s/github/dojo/dojo-codesandbox-template`; export const newPreactSandboxUrl = () => `/s/preact`; export const newVueSandboxUrl = () => `/s/vue`; export const importFromGitHubUrl = () => `/s/github`; export const newSvelteSandboxUrl = () => `/s/svelte`; export const newAngularSandboxUrl = () => `/s/angular`; +export const newCxJSSandboxUrl = () => + `/s/github/codaxy/cxjs-codesandbox-template`; export const uploadFromCliUrl = () => `/s/cli`; const sandboxGitUrl = (git: { diff --git a/packages/homepage/content/changelog/2018-04-25-dojo-and-cxjs-templates.md b/packages/homepage/content/changelog/2018-04-25-dojo-and-cxjs-templates.md new file mode 100644 index 00000000000..755868db215 --- /dev/null +++ b/packages/homepage/content/changelog/2018-04-25-dojo-and-cxjs-templates.md @@ -0,0 +1,6 @@ +--- +title: "Dojo 2 and CxJS templates" +authors: ["nicknisi", "mstijak"] +--- + +Not one, but two new templates have been added to CodeSandbox! The templates are [Dojo](https://dojo.io/) and [CxJS](https://cxjs.io/). These implementations are completely built by @nicknisi and @mstijak. A huge thanks for their effort! diff --git a/packages/homepage/src/screens/home/Animation/Background.js b/packages/homepage/src/screens/home/Animation/Background.js index f470db888f0..1c63056afc5 100644 --- a/packages/homepage/src/screens/home/Animation/Background.js +++ b/packages/homepage/src/screens/home/Animation/Background.js @@ -28,6 +28,7 @@ export default class Background extends React.PureComponent { // Use solid colors for perf colors = { 'create-react-app': '#1E2428', + '@dojo/cli-create-app': '#211D1C', 'vue-cli': '#1D2525', 'preact-cli': '#202328', svelte: '#202022', diff --git a/packages/homepage/src/screens/home/Animation/index.js b/packages/homepage/src/screens/home/Animation/index.js index 820b716d68e..c267b6ccec1 100644 --- a/packages/homepage/src/screens/home/Animation/index.js +++ b/packages/homepage/src/screens/home/Animation/index.js @@ -14,6 +14,8 @@ import { preact, svelte, parcel, + cxjs, + dojo, } from 'common/templates'; import Background from './Background'; @@ -64,7 +66,17 @@ const Message = styled.div` `}; `; -const TEMPLATES = [parcel, react, vue, angular, preact, reactTs, svelte]; +const TEMPLATES = [ + parcel, + react, + vue, + angular, + preact, + reactTs, + svelte, + cxjs, + dojo, +]; export default class Animation extends React.PureComponent { state = { diff --git a/packages/homepage/src/screens/home/Frameworks/index.js b/packages/homepage/src/screens/home/Frameworks/index.js index c3ac2d569b9..5c84e842cb5 100644 --- a/packages/homepage/src/screens/home/Frameworks/index.js +++ b/packages/homepage/src/screens/home/Frameworks/index.js @@ -207,6 +207,16 @@ const TEMPLATE_SUPPORT = { css: ['Global'], description: 'Used for React projects, based on: ', }, + '@dojo/cli-create-app': { + loaders: [ts, html, cssGlobal, image], + css: ['Global', 'Scoped', 'Modules'], + description: 'Used for Dojo 2 projects, based on: ', + }, + cxjs: { + loaders: [ts, html, cssGlobal, scss, sass, less, stylus, image], + css: ['Global', 'Scoped', 'Modules'], + description: 'Used for CxJS projects, based on: ', + }, 'vue-cli': { loaders: [ js, diff --git a/yarn.lock b/yarn.lock index e4040698d26..aaa4123def7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2121,6 +2121,12 @@ babel-plugin-transform-class-properties@6.24.1, babel-plugin-transform-class-pro babel-runtime "^6.22.0" babel-template "^6.24.1" +babel-plugin-transform-cx-jsx@^17.12.0: + version "17.12.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-cx-jsx/-/babel-plugin-transform-cx-jsx-17.12.0.tgz#a023e1c40e54f5cd8da5efc5cfeab3caa9e27695" + dependencies: + babel-plugin-syntax-jsx "^6.18.0" + babel-plugin-transform-decorators-legacy@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.4.tgz#741b58f6c5bce9e6027e0882d9c994f04f366925"