mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Re-adds Flow parsing and stripping when option is enabled
Summary: Release notes: adds option to parse and strip Flow annotations In a [tweet I made at the end of last year](https://twitter.com/trueadm/status/944908776896978946) I linked to a REPL example that contained Flow type annotations. The example is now broken as we removed Flow type annotation parsing. :( This PR adds this back behind the option `stripFlow` which is now enabled by default on the REPL and for React tests. I've also added a test to match the REPL for regression checking so this doesn't happen. Closes https://github.com/facebook/prepack/pull/1439 Differential Revision: D6976663 Pulled By: trueadm fbshipit-source-id: 47e28bc0986f1a1a252180f26200ce32314a4e5b
This commit is contained in:
parent
d8908e31e6
commit
414cc67b93
@ -329,6 +329,13 @@ ReactStatistics {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Test React (JSX) fb-www mocks repl example 1`] = `
|
||||||
|
ReactStatistics {
|
||||||
|
"inlinedComponents": 6,
|
||||||
|
"optimizedTrees": 1,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Test React (create-element) Class component folding Classes with state 1`] = `
|
exports[`Test React (create-element) Class component folding Classes with state 1`] = `
|
||||||
ReactStatistics {
|
ReactStatistics {
|
||||||
"inlinedComponents": 0,
|
"inlinedComponents": 0,
|
||||||
@ -657,3 +664,10 @@ ReactStatistics {
|
|||||||
"optimizedTrees": 2,
|
"optimizedTrees": 2,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Test React (create-element) fb-www mocks repl example 1`] = `
|
||||||
|
ReactStatistics {
|
||||||
|
"inlinedComponents": 6,
|
||||||
|
"optimizedTrees": 1,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -53,6 +53,7 @@ function runTestSuite(outputJsx) {
|
|||||||
inlineExpressions: true,
|
inlineExpressions: true,
|
||||||
omitInvariants: true,
|
omitInvariants: true,
|
||||||
abstractEffectsInAdditionalFunctions: true,
|
abstractEffectsInAdditionalFunctions: true,
|
||||||
|
stripFlow: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
function compileSourceWithPrepack(source) {
|
function compileSourceWithPrepack(source) {
|
||||||
@ -374,6 +375,10 @@ function runTestSuite(outputJsx) {
|
|||||||
it("fb-www 8", async () => {
|
it("fb-www 8", async () => {
|
||||||
await runTest(directory, "fb8.js");
|
await runTest(directory, "fb8.js");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("repl example", async () => {
|
||||||
|
await runTest(directory, "repl-example.js");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ export type RealmOptions = {
|
|||||||
maxStackDepth?: number,
|
maxStackDepth?: number,
|
||||||
reactEnabled?: boolean,
|
reactEnabled?: boolean,
|
||||||
reactOutput?: ReactOutputTypes,
|
reactOutput?: ReactOutputTypes,
|
||||||
|
stripFlow?: boolean,
|
||||||
abstractEffectsInAdditionalFunctions?: boolean,
|
abstractEffectsInAdditionalFunctions?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ export type PrepackOptions = {|
|
|||||||
initializeMoreModules?: boolean,
|
initializeMoreModules?: boolean,
|
||||||
statsFile?: string,
|
statsFile?: string,
|
||||||
strictlyMonotonicDateNow?: boolean,
|
strictlyMonotonicDateNow?: boolean,
|
||||||
|
stripFlow?: boolean,
|
||||||
timeout?: number,
|
timeout?: number,
|
||||||
trace?: boolean,
|
trace?: boolean,
|
||||||
uniqueSuffix?: string,
|
uniqueSuffix?: string,
|
||||||
@ -70,6 +71,7 @@ export function getRealmOptions({
|
|||||||
serialize = !residual,
|
serialize = !residual,
|
||||||
check,
|
check,
|
||||||
strictlyMonotonicDateNow,
|
strictlyMonotonicDateNow,
|
||||||
|
stripFlow,
|
||||||
timeout,
|
timeout,
|
||||||
maxStackDepth,
|
maxStackDepth,
|
||||||
}: PrepackOptions): RealmOptions {
|
}: PrepackOptions): RealmOptions {
|
||||||
@ -88,6 +90,7 @@ export function getRealmOptions({
|
|||||||
serialize,
|
serialize,
|
||||||
check,
|
check,
|
||||||
strictlyMonotonicDateNow,
|
strictlyMonotonicDateNow,
|
||||||
|
stripFlow,
|
||||||
timeout,
|
timeout,
|
||||||
maxStackDepth,
|
maxStackDepth,
|
||||||
};
|
};
|
||||||
|
@ -191,6 +191,8 @@ export class Realm {
|
|||||||
hoistableFunctions: new WeakMap(),
|
hoistableFunctions: new WeakMap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.stripFlow = opts.stripFlow || false;
|
||||||
|
|
||||||
this.fbLibraries = {
|
this.fbLibraries = {
|
||||||
other: new Map(),
|
other: new Map(),
|
||||||
react: undefined,
|
react: undefined,
|
||||||
@ -255,6 +257,7 @@ export class Realm {
|
|||||||
output?: ReactOutputTypes,
|
output?: ReactOutputTypes,
|
||||||
symbols: Map<ReactSymbolTypes, SymbolValue>,
|
symbols: Map<ReactSymbolTypes, SymbolValue>,
|
||||||
};
|
};
|
||||||
|
stripFlow: boolean;
|
||||||
|
|
||||||
fbLibraries: {
|
fbLibraries: {
|
||||||
other: Map<string, AbstractValue>,
|
other: Map<string, AbstractValue>,
|
||||||
|
@ -23,6 +23,7 @@ import type { ReactSerializerState, SerializedResult } from "./types.js";
|
|||||||
import { Functions } from "./functions.js";
|
import { Functions } from "./functions.js";
|
||||||
import { Logger } from "../utils/logger.js";
|
import { Logger } from "../utils/logger.js";
|
||||||
import { Modules } from "../utils/modules.js";
|
import { Modules } from "../utils/modules.js";
|
||||||
|
import { stripFlowTypeAnnotations } from "../utils/flow.js";
|
||||||
import { LoggingTracer } from "./LoggingTracer.js";
|
import { LoggingTracer } from "./LoggingTracer.js";
|
||||||
import { ResidualHeapVisitor } from "./ResidualHeapVisitor.js";
|
import { ResidualHeapVisitor } from "./ResidualHeapVisitor.js";
|
||||||
import { ResidualHeapSerializer } from "./ResidualHeapSerializer.js";
|
import { ResidualHeapSerializer } from "./ResidualHeapSerializer.js";
|
||||||
@ -224,6 +225,9 @@ export class Serializer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let ast = residualHeapSerializer.serialize();
|
let ast = residualHeapSerializer.serialize();
|
||||||
|
if (this.realm.stripFlow) {
|
||||||
|
stripFlowTypeAnnotations(ast);
|
||||||
|
}
|
||||||
|
|
||||||
// the signature for generate is not complete, hence the any
|
// the signature for generate is not complete, hence the any
|
||||||
let generated = generate(ast, { sourceMaps: sourceMaps }, (code: any));
|
let generated = generate(ast, { sourceMaps: sourceMaps }, (code: any));
|
||||||
|
78
src/utils/flow.js
Normal file
78
src/utils/flow.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2017-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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @flow */
|
||||||
|
|
||||||
|
import traverse from "babel-traverse";
|
||||||
|
import { BabelNode } from "babel-types";
|
||||||
|
import * as t from "babel-types";
|
||||||
|
|
||||||
|
// Taken directly from Babel:
|
||||||
|
// https://github.com/babel/babel/blob/cde005422701a69ff21044c138c29a5ad23b6d0a/packages/babel-plugin-transform-flow-strip-types/src/index.js#L32-L107
|
||||||
|
// Copyright 2015-present Sebastian McKenzie / Babel project (https://github.com/babel)
|
||||||
|
// only the lines reflected in the above were used
|
||||||
|
export function stripFlowTypeAnnotations(ast: BabelNode): void {
|
||||||
|
traverse(
|
||||||
|
ast,
|
||||||
|
{
|
||||||
|
ImportDeclaration(path) {
|
||||||
|
if (!path.node.specifiers.length) return;
|
||||||
|
let typeCount = 0;
|
||||||
|
path.node.specifiers.forEach(({ importKind }) => {
|
||||||
|
if (importKind === "type" || importKind === "typeof") {
|
||||||
|
typeCount++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (typeCount === path.node.specifiers.length) {
|
||||||
|
path.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Flow(path) {
|
||||||
|
path.remove();
|
||||||
|
},
|
||||||
|
ClassProperty(path) {
|
||||||
|
path.node.variance = null;
|
||||||
|
path.node.typeAnnotation = null;
|
||||||
|
if (!path.node.value) path.remove();
|
||||||
|
},
|
||||||
|
Class(path) {
|
||||||
|
path.node.implements = null;
|
||||||
|
path.get("body.body").forEach(child => {
|
||||||
|
if (child.isClassProperty()) {
|
||||||
|
child.node.typeAnnotation = null;
|
||||||
|
if (!child.node.value) child.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
AssignmentPattern({ node }) {
|
||||||
|
node.left.optional = false;
|
||||||
|
},
|
||||||
|
Function({ node }) {
|
||||||
|
for (let i = 0; i < node.params.length; i++) {
|
||||||
|
const param = node.params[i];
|
||||||
|
param.optional = false;
|
||||||
|
if (param.type === "AssignmentPattern") {
|
||||||
|
param.left.optional = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.predicate = null;
|
||||||
|
},
|
||||||
|
TypeCastExpression(path) {
|
||||||
|
let { node } = path;
|
||||||
|
do {
|
||||||
|
node = node.expression;
|
||||||
|
} while (t.isTypeCastExpression(node));
|
||||||
|
path.replaceWith(node);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
(undefined: any),
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
}
|
@ -31,6 +31,9 @@ export default function(
|
|||||||
if (realm.react.enabled) {
|
if (realm.react.enabled) {
|
||||||
plugins.push("jsx");
|
plugins.push("jsx");
|
||||||
}
|
}
|
||||||
|
if (realm.stripFlow) {
|
||||||
|
plugins.push("flow");
|
||||||
|
}
|
||||||
let ast = parse(code, { filename, sourceType, startLine, plugins });
|
let ast = parse(code, { filename, sourceType, startLine, plugins });
|
||||||
traverseFast(ast, node => {
|
traverseFast(ast, node => {
|
||||||
invariant(node.loc);
|
invariant(node.loc);
|
||||||
|
35
test/react/mocks/repl-example.js
Normal file
35
test/react/mocks/repl-example.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
var React = require('React');
|
||||||
|
// the JSX transform converts to React, so we need to add it back in
|
||||||
|
this['React'] = React;
|
||||||
|
|
||||||
|
function Yar(props) {
|
||||||
|
return <a href={props.href}><em>{props.name}</em></a>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Bar(props) {
|
||||||
|
return <div><span style={{color: "red"}}>Here's a link</span>: <Yar {...props} /></div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for now, we require inline Flow type annotations on the root component
|
||||||
|
// for its props (if it has any)
|
||||||
|
function Foo(props: {href: string}) {
|
||||||
|
var collection = [
|
||||||
|
{ href: props.href, name: "First Item" },
|
||||||
|
{ href: props.href, name: "Second Item" },
|
||||||
|
{ href: props.href, name: "Third Item" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{collection.map(item => <Bar {...item} /> )}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a special Prepack function hook
|
||||||
|
// that tells Prepack the root of a React component tree
|
||||||
|
if (this.__registerReactComponentRoot) {
|
||||||
|
__registerReactComponentRoot(Foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Foo = Foo;
|
@ -63,6 +63,12 @@ var optionsConfig = [
|
|||||||
defaultVal: "jsx",
|
defaultVal: "jsx",
|
||||||
description: "Specifies the serialization output of JSX nodes when React mode is enabled."
|
description: "Specifies the serialization output of JSX nodes when React mode is enabled."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "boolean",
|
||||||
|
name: "stripFlow",
|
||||||
|
defaultVal: true,
|
||||||
|
description: "Removes Flow type annotations from the output."
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
var demos = [];
|
var demos = [];
|
||||||
|
Loading…
Reference in New Issue
Block a user