From 8928d403ea1e0c1449703464ee31ca9699d424fd Mon Sep 17 00:00:00 2001 From: Mattia Manzati Date: Sun, 28 Aug 2016 15:41:43 +0200 Subject: [PATCH 1/4] feat: init TypeScript support --- config/typescript.dev.js | 19 + config/typescript.prod.js | 15 + config/webpack.config.dev.js | 17 +- config/webpack.config.prod.js | 17 +- package.json | 4 + template/src/App.js | 2 + template/src/HelloTypeScript.tsx | 8 + template/tsconfig.json | 12 + template/typings.json | 7 + template/typings/index.d.ts | 2 + template/typings/modules/react-dom/index.d.ts | 2418 +++++++++++++++++ .../typings/modules/react-dom/typings.json | 22 + template/typings/modules/react/index.d.ts | 2323 ++++++++++++++++ template/typings/modules/react/typings.json | 10 + 14 files changed, 4872 insertions(+), 4 deletions(-) create mode 100644 config/typescript.dev.js create mode 100644 config/typescript.prod.js create mode 100644 template/src/HelloTypeScript.tsx create mode 100644 template/tsconfig.json create mode 100644 template/typings.json create mode 100644 template/typings/index.d.ts create mode 100644 template/typings/modules/react-dom/index.d.ts create mode 100644 template/typings/modules/react-dom/typings.json create mode 100644 template/typings/modules/react/index.d.ts create mode 100644 template/typings/modules/react/typings.json diff --git a/config/typescript.dev.js b/config/typescript.dev.js new file mode 100644 index 00000000000..eefd2f23bcb --- /dev/null +++ b/config/typescript.dev.js @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +// strip cacheDirectory since it's a babel-loader specific option +var babelOptions = Object.assign({}, require('./babel.dev')) +delete babelOptions['cacheDirectory'] + +module.exports = { + // when TypeScript emits a file, pass it to Babel to provide backwards compatibility + useBabel: true, + // these are the options to use + babelOptions: babelOptions +}; \ No newline at end of file diff --git a/config/typescript.prod.js b/config/typescript.prod.js new file mode 100644 index 00000000000..915626ae626 --- /dev/null +++ b/config/typescript.prod.js @@ -0,0 +1,15 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +module.exports = { + // when TypeScript emits a file, pass it to Babel to provide backwards compatibility + useBabel: true, + // these are the options to use + babelOptions: require('./babel.prod') +}; \ No newline at end of file diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index b93abcdad20..5bfccbd4892 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -66,7 +66,8 @@ module.exports = { }, resolve: { // These are the reasonable defaults supported by the Node ecosystem. - extensions: ['.js', '.json', ''], + // We support also TypeScript ts and tsx which will be compiled later + extensions: ['.ts', '.tsx', '.js', '.json', ''], alias: { // This `alias` section can be safely removed after ejection. // We do this because `babel-runtime` may be inside `react-scripts`, @@ -89,15 +90,27 @@ module.exports = { }, module: { // First, run the linter. - // It's important to do this before Babel processes the JS. + // It's important to do this before Babel or TypeScript processes the JS/TS. preLoaders: [ { test: /\.js$/, loader: 'eslint', include: paths.appSrc, + }, + { + test: /\.tsx?$/, + loader: 'tslint', + include: paths.appSrc } ], loaders: [ + // Process TS with TypeScript and then with Babel + { + test: /\.tsx?$/, + include: paths.appSrc, + loader: 'awesome-typescript', + query: require('./typescript.dev') + }, // Process JS with Babel. { test: /\.js$/, diff --git a/config/webpack.config.prod.js b/config/webpack.config.prod.js index 8b04ec4b650..5cc229fafd4 100644 --- a/config/webpack.config.prod.js +++ b/config/webpack.config.prod.js @@ -61,7 +61,8 @@ module.exports = { }, resolve: { // These are the reasonable defaults supported by the Node ecosystem. - extensions: ['.js', '.json', ''], + // We support also TypeScript ts and tsx which will be compiled later + extensions: ['.ts', '.tsx', '.js', '.json', ''], alias: { // This `alias` section can be safely removed after ejection. // We do this because `babel-runtime` may be inside `react-scripts`, @@ -84,15 +85,27 @@ module.exports = { }, module: { // First, run the linter. - // It's important to do this before Babel processes the JS. + // It's important to do this before Babel or TypeScript processes the JS/TS. preLoaders: [ { test: /\.js$/, loader: 'eslint', include: paths.appSrc + }, + { + test: /\.tsx?$/, + loader: 'tslint', + include: paths.appSrc } ], loaders: [ + // Process TS with TypeScript and then with Babel + { + test: /\.tsx?$/, + include: paths.appSrc, + loader: 'awesome-typescript', + query: require('./typescript.prod') + }, // Process JS with Babel. { test: /\.js$/, diff --git a/package.json b/package.json index 1694ded2b12..dcfe9028145 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "autoprefixer": "6.4.0", + "awesome-typescript-loader": "^2.2.1", "babel-core": "6.14.0", "babel-eslint": "6.1.2", "babel-jest": "14.1.0", @@ -72,6 +73,9 @@ "rimraf": "2.5.4", "strip-ansi": "3.0.1", "style-loader": "0.13.1", + "tslint": "^3.15.1", + "tslint-loader": "^2.1.5", + "typescript": "^2.0.0", "url-loader": "0.5.7", "webpack": "1.13.1", "webpack-dev-server": "1.14.1", diff --git a/template/src/App.js b/template/src/App.js index d7d52a7f38a..df18fdcf84a 100644 --- a/template/src/App.js +++ b/template/src/App.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import {HelloTypeScript} from './HelloTypeScript' import logo from './logo.svg'; import './App.css'; @@ -13,6 +14,7 @@ class App extends Component {

To get started, edit src/App.js and save to reload.

+ ); } diff --git a/template/src/HelloTypeScript.tsx b/template/src/HelloTypeScript.tsx new file mode 100644 index 00000000000..ee86104a884 --- /dev/null +++ b/template/src/HelloTypeScript.tsx @@ -0,0 +1,8 @@ +import * as React from "react"; +const {Component} = React; + +export class HelloTypeScript extends Component { + render() { + return

If you'd like to, you can also use TypeScript!

; + } +} \ No newline at end of file diff --git a/template/tsconfig.json b/template/tsconfig.json new file mode 100644 index 00000000000..fead9aee27c --- /dev/null +++ b/template/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es2015", + "jsx": "react" + }, + "include": [ + "src" + ], + "files": [ + "./typings/index.d.ts" + ] +} \ No newline at end of file diff --git a/template/typings.json b/template/typings.json new file mode 100644 index 00000000000..692ed2d9e70 --- /dev/null +++ b/template/typings.json @@ -0,0 +1,7 @@ +{ + "name": "create-react-app", + "dependencies": { + "react": "registry:npm/react#15.0.1+20160601175240", + "react-dom": "registry:npm/react-dom#15.0.1+20160826174104" + } +} diff --git a/template/typings/index.d.ts b/template/typings/index.d.ts new file mode 100644 index 00000000000..218b1301bcb --- /dev/null +++ b/template/typings/index.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/template/typings/modules/react-dom/index.d.ts b/template/typings/modules/react-dom/index.d.ts new file mode 100644 index 00000000000..89b86ab410c --- /dev/null +++ b/template/typings/modules/react-dom/index.d.ts @@ -0,0 +1,2418 @@ +// Generated by typings +// Source: https://raw.githubusercontent.com/cantide5ga/typed-react-dom/d498c5a1056d26babef04b56139628be1f59a357/server.d.ts +declare module '~react-dom/server' { +import { ReactElement } from '~react-dom~react'; + +namespace ReactDomServer { + function renderToString(element: ReactElement): string; + function renderToStaticMarkup(element: ReactElement): string; + var version: string; +} + +export = ReactDomServer; +} +declare module 'react-dom/server' { +import main = require('~react-dom/server'); +export = main; +} + +// Generated by typings +// Source: https://raw.githubusercontent.com/cantide5ga/typed-react/42692d400db3c333394ec75bce9f6d09b5a0a769/react.d.ts +declare module '~react-dom~react' { +// Type definitions for React v0.14 +// Project: http://facebook.github.io/react/ +// Definitions by: Asana , AssureSign , Microsoft +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// Typings: Michael N. Payne + +namespace React { + + // + // React Elements + // ---------------------------------------------------------------------- + + type ReactType = string | ComponentClass | StatelessComponent; + + type Key = string | number; + type Ref = string | ((instance: T) => any); + type ComponentState = {} | void; + + interface Attributes { + key?: Key; + } + interface ClassAttributes extends Attributes { + ref?: Ref; + } + + interface ReactElement

{ + type: string | ComponentClass

| SFC

; + props: P; + key?: Key; + } + + interface SFCElement

extends ReactElement

{ + type: SFC

; + } + + type CElement> = ComponentElement; + interface ComponentElement> extends ReactElement

{ + type: ComponentClass

; + ref?: Ref; + } + + type ClassicElement

= CElement>; + + interface DOMElement

extends ReactElement

{ + type: string; + ref: Ref; + } + + interface ReactHTMLElement extends DOMElement { + } + + interface ReactSVGElement extends DOMElement { + } + + // + // Factories + // ---------------------------------------------------------------------- + + interface Factory

{ + (props?: P & Attributes, ...children: ReactNode[]): ReactElement

; + } + + interface SFCFactory

{ + (props?: P & Attributes, ...children: ReactNode[]): SFCElement

; + } + + interface ComponentFactory> { + (props?: P & ClassAttributes, ...children: ReactNode[]): CElement; + } + + type CFactory> = ComponentFactory; + type ClassicFactory

= CFactory>; + + interface DOMFactory

{ + (props?: P & ClassAttributes, ...children: ReactNode[]): DOMElement; + } + + interface HTMLFactory extends DOMFactory { + } + + interface SVGFactory extends DOMFactory { + } + + // + // React Nodes + // http://facebook.github.io/react/docs/glossary.html + // ---------------------------------------------------------------------- + + type ReactText = string | number; + type ReactChild = ReactElement | ReactText; + + // Should be Array but type aliases cannot be recursive + type ReactFragment = {} | Array; + type ReactNode = ReactChild | ReactFragment | boolean; + + // + // Top Level API + // ---------------------------------------------------------------------- + + function createClass(spec: ComponentSpec): ClassicComponentClass

; + + function createFactory

( + type: string): DOMFactory; + function createFactory

(type: SFC

): SFCFactory

; + function createFactory

( + type: ClassType, ClassicComponentClass

>): CFactory>; + function createFactory, C extends ComponentClass

>( + type: ClassType): CFactory; + function createFactory

(type: ComponentClass

| SFC

): Factory

; + + function createElement

( + type: string, + props?: P & ClassAttributes, + ...children: ReactNode[]): DOMElement; + function createElement

( + type: SFC

, + props?: P & Attributes, + ...children: ReactNode[]): SFCElement

; + function createElement

( + type: ClassType, ClassicComponentClass

>, + props?: P & ClassAttributes>, + ...children: ReactNode[]): CElement>; + function createElement, C extends ComponentClass

>( + type: ClassType, + props?: P & ClassAttributes, + ...children: ReactNode[]): CElement; + function createElement

( + type: ComponentClass

| SFC

, + props?: P & Attributes, + ...children: ReactNode[]): ReactElement

; + + function cloneElement

( + element: DOMElement, + props?: P & ClassAttributes, + ...children: ReactNode[]): DOMElement; + function cloneElement

( + element: SFCElement

, + props?: Q, // should be Q & Attributes, but then Q is inferred as {} + ...children: ReactNode[]): SFCElement

; + function cloneElement

>( + element: CElement, + props?: Q, // should be Q & ClassAttributes + ...children: ReactNode[]): CElement; + function cloneElement

( + element: ReactElement

, + props?: Q, // should be Q & Attributes + ...children: ReactNode[]): ReactElement

; + + function isValidElement

(object: {}): object is ReactElement

; + + var DOM: ReactDOM; + var PropTypes: ReactPropTypes; + var Children: ReactChildren; + + // + // Component API + // ---------------------------------------------------------------------- + + type ReactInstance = Component | Element; + + // Base component for plain JS classes + class Component implements ComponentLifecycle { + constructor(props?: P, context?: any); + setState(f: (prevState: S, props: P) => S, callback?: () => any): void; + setState(state: S, callback?: () => any): void; + forceUpdate(callBack?: () => any): void; + render(): ReactElement; + + // React.Props is now deprecated, which means that the `children` + // property is not available on `P` by default, even though you can + // always pass children as variadic arguments to `createElement`. + // In the future, if we can define its call signature conditionally + // on the existence of `children` in `P`, then we should remove this. + props: P & { children?: ReactNode }; + state: S; + context: {}; + refs: { + [key: string]: ReactInstance + }; + } + + interface ClassicComponent extends Component { + replaceState(nextState: S, callback?: () => any): void; + isMounted(): boolean; + getInitialState?(): S; + } + + interface ChildContextProvider { + getChildContext(): CC; + } + + // + // Class Interfaces + // ---------------------------------------------------------------------- + + type SFC

= StatelessComponent

; + interface StatelessComponent

{ + (props?: P, context?: any): ReactElement; + propTypes?: ValidationMap

; + contextTypes?: ValidationMap; + defaultProps?: P; + displayName?: string; + } + + interface ComponentClass

{ + new(props?: P, context?: any): Component; + propTypes?: ValidationMap

; + contextTypes?: ValidationMap; + childContextTypes?: ValidationMap; + defaultProps?: P; + displayName?: string; + } + + interface ClassicComponentClass

extends ComponentClass

{ + new(props?: P, context?: any): ClassicComponent; + getDefaultProps?(): P; + } + + /** + * We use an intersection type to infer multiple type parameters from + * a single argument, which is useful for many top-level API defs. + * See https://github.com/Microsoft/TypeScript/issues/7234 for more info. + */ + type ClassType, C extends ComponentClass

> = + C & + (new() => T) & + (new() => { props: P }); + + // + // Component Specs and Lifecycle + // ---------------------------------------------------------------------- + + interface ComponentLifecycle { + componentWillMount?(): void; + componentDidMount?(): void; + componentWillReceiveProps?(nextProps: P, nextContext: any): void; + shouldComponentUpdate?(nextProps: P, nextState: S, nextContext: any): boolean; + componentWillUpdate?(nextProps: P, nextState: S, nextContext: any): void; + componentDidUpdate?(prevProps: P, prevState: S, prevContext: any): void; + componentWillUnmount?(): void; + } + + interface Mixin extends ComponentLifecycle { + mixins?: Mixin; + statics?: { + [key: string]: any; + }; + + displayName?: string; + propTypes?: ValidationMap; + contextTypes?: ValidationMap; + childContextTypes?: ValidationMap; + + getDefaultProps?(): P; + getInitialState?(): S; + } + + interface ComponentSpec extends Mixin { + render(): ReactElement; + + [propertyName: string]: any; + } + + // + // Event System + // ---------------------------------------------------------------------- + + interface SyntheticEvent { + bubbles: boolean; + cancelable: boolean; + currentTarget: EventTarget; + defaultPrevented: boolean; + eventPhase: number; + isTrusted: boolean; + nativeEvent: Event; + preventDefault(): void; + stopPropagation(): void; + target: EventTarget; + timeStamp: Date; + type: string; + } + + interface ClipboardEvent extends SyntheticEvent { + clipboardData: DataTransfer; + } + + interface CompositionEvent extends SyntheticEvent { + data: string; + } + + interface DragEvent extends SyntheticEvent { + dataTransfer: DataTransfer; + } + + interface FocusEvent extends SyntheticEvent { + relatedTarget: EventTarget; + } + + interface FormEvent extends SyntheticEvent { + } + + interface KeyboardEvent extends SyntheticEvent { + altKey: boolean; + charCode: number; + ctrlKey: boolean; + getModifierState(key: string): boolean; + key: string; + keyCode: number; + locale: string; + location: number; + metaKey: boolean; + repeat: boolean; + shiftKey: boolean; + which: number; + } + + interface MouseEvent extends SyntheticEvent { + altKey: boolean; + button: number; + buttons: number; + clientX: number; + clientY: number; + ctrlKey: boolean; + getModifierState(key: string): boolean; + metaKey: boolean; + pageX: number; + pageY: number; + relatedTarget: EventTarget; + screenX: number; + screenY: number; + shiftKey: boolean; + } + + interface TouchEvent extends SyntheticEvent { + altKey: boolean; + changedTouches: TouchList; + ctrlKey: boolean; + getModifierState(key: string): boolean; + metaKey: boolean; + shiftKey: boolean; + targetTouches: TouchList; + touches: TouchList; + } + + interface UIEvent extends SyntheticEvent { + detail: number; + view: AbstractView; + } + + interface WheelEvent extends SyntheticEvent { + deltaMode: number; + deltaX: number; + deltaY: number; + deltaZ: number; + } + + // + // Event Handler Types + // ---------------------------------------------------------------------- + + interface EventHandler { + (event: E): void; + } + + type ReactEventHandler = EventHandler; + + type ClipboardEventHandler = EventHandler; + type CompositionEventHandler = EventHandler; + type DragEventHandler = EventHandler; + type FocusEventHandler = EventHandler; + type FormEventHandler = EventHandler; + type KeyboardEventHandler = EventHandler; + type MouseEventHandler = EventHandler; + type TouchEventHandler = EventHandler; + type UIEventHandler = EventHandler; + type WheelEventHandler = EventHandler; + + // + // Props / DOM Attributes + // ---------------------------------------------------------------------- + + /** + * @deprecated. This was used to allow clients to pass `ref` and `key` + * to `createElement`, which is no longer necessary due to intersection + * types. If you need to declare a props object before passing it to + * `createElement` or a factory, use `ClassAttributes`: + * + * ```ts + * var b: Button; + * var props: ButtonProps & ClassAttributes