@flamework-experimental/transformer 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/flamework-schema.json +76 -0
- package/flamework.config.schema.json +197 -0
- package/out/classes/buildInfo.js +467 -0
- package/out/classes/diagnostics.js +110 -0
- package/out/classes/logger.js +201 -0
- package/out/classes/nodeMetadata.js +232 -0
- package/out/classes/transformState.js +623 -0
- package/out/index.js +75 -0
- package/out/transformations/expressions/transformAccessExpression.js +35 -0
- package/out/transformations/expressions/transformBinaryExpression.js +57 -0
- package/out/transformations/expressions/transformCallExpression.js +33 -0
- package/out/transformations/expressions/transformDeleteExpression.js +22 -0
- package/out/transformations/expressions/transformNewExpression.js +17 -0
- package/out/transformations/expressions/transformUnaryExpression.js +63 -0
- package/out/transformations/macros/intrinsics/components.js +64 -0
- package/out/transformations/macros/intrinsics/env.js +31 -0
- package/out/transformations/macros/intrinsics/guards.js +43 -0
- package/out/transformations/macros/intrinsics/inlining.js +22 -0
- package/out/transformations/macros/intrinsics/networking.js +131 -0
- package/out/transformations/macros/intrinsics/parameters.js +81 -0
- package/out/transformations/macros/intrinsics/paths.js +42 -0
- package/out/transformations/macros/updateComponentConfig.js +409 -0
- package/out/transformations/plugins/nodeFactory.js +159 -0
- package/out/transformations/plugins/pluginHost.js +235 -0
- package/out/transformations/plugins/typeFacade.js +242 -0
- package/out/transformations/statements/transformClassDeclaration.js +282 -0
- package/out/transformations/transformExpression.js +34 -0
- package/out/transformations/transformFile.js +78 -0
- package/out/transformations/transformNetworkingCall.js +566 -0
- package/out/transformations/transformNode.js +27 -0
- package/out/transformations/transformStatement.js +57 -0
- package/out/transformations/transformStatementList.js +64 -0
- package/out/transformations/transformUserMacro.js +698 -0
- package/out/transformer.js +52 -0
- package/out/util/cache.js +10 -0
- package/out/util/constants.js +5 -0
- package/out/util/diagnosticsUtils.js +112 -0
- package/out/util/env.js +267 -0
- package/out/util/factory.js +583 -0
- package/out/util/functions/addLeadingComment.js +13 -0
- package/out/util/functions/arePathsEqual.js +14 -0
- package/out/util/functions/assert.js +17 -0
- package/out/util/functions/buildGuardFromType.js +713 -0
- package/out/util/functions/buildInstanceShape.js +156 -0
- package/out/util/functions/buildSerializerFromType.js +2309 -0
- package/out/util/functions/createPathTranslator.js +54 -0
- package/out/util/functions/emitTypescriptMismatch.js +80 -0
- package/out/util/functions/getDeclarationName.js +23 -0
- package/out/util/functions/getDeclarationOfType.js +7 -0
- package/out/util/functions/getIndexExpression.js +16 -0
- package/out/util/functions/getInstanceTypeFromType.js +75 -0
- package/out/util/functions/getNodeList.js +6 -0
- package/out/util/functions/getPackageJson.js +44 -0
- package/out/util/functions/getSuperClasses.js +51 -0
- package/out/util/functions/isAttributesAccess.js +23 -0
- package/out/util/functions/isCleanBuildDirectory.js +13 -0
- package/out/util/functions/isDefinedType.js +16 -0
- package/out/util/functions/isPathDescendantOf.js +25 -0
- package/out/util/functions/isTupleType.js +10 -0
- package/out/util/functions/parseCommandLine.js +31 -0
- package/out/util/functions/shuffle.js +39 -0
- package/out/util/functions/tryResolve.js +15 -0
- package/out/util/functions/validateConstraintMetadata.js +60 -0
- package/out/util/packages.js +12 -0
- package/out/util/projectConfig.js +221 -0
- package/out/util/schema.js +80 -0
- package/out/util/tsInternals.js +16 -0
- package/out/util/uid.js +227 -0
- package/package.json +45 -0
- package/rojo-schema.json +34 -0
- package/types.d.ts +4 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __values = (this && this.__values) || function(o) {
|
|
3
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
+
if (m) return m.call(o);
|
|
5
|
+
if (o && typeof o.length === "number") return {
|
|
6
|
+
next: function () {
|
|
7
|
+
if (o && i >= o.length) o = void 0;
|
|
8
|
+
return { value: o && o[i++], done: !o };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.validateParameterConstIntrinsic = validateParameterConstIntrinsic;
|
|
18
|
+
var typescript_1 = __importDefault(require("typescript"));
|
|
19
|
+
var diagnostics_1 = require("../../../classes/diagnostics");
|
|
20
|
+
/**
|
|
21
|
+
* Validates that the specified parameters can be inspected at compile-time (up to a depth of 1)
|
|
22
|
+
*/
|
|
23
|
+
function validateParameterConstIntrinsic(node, signature, parameters) {
|
|
24
|
+
var e_1, _a;
|
|
25
|
+
var _b, _c;
|
|
26
|
+
var _loop_1 = function (parameter) {
|
|
27
|
+
var e_2, _d;
|
|
28
|
+
var parameterIndex = signature.parameters.findIndex(function (v) { var _a; return ((_a = v.valueDeclaration) === null || _a === void 0 ? void 0 : _a.symbol) === parameter; });
|
|
29
|
+
var argument = (_b = node.arguments) === null || _b === void 0 ? void 0 : _b[parameterIndex];
|
|
30
|
+
if (!argument) {
|
|
31
|
+
return "continue";
|
|
32
|
+
}
|
|
33
|
+
// Check if the argument is a literal (string, number, etc)
|
|
34
|
+
if (typescript_1.default.isLiteralExpression(argument)) {
|
|
35
|
+
return "continue";
|
|
36
|
+
}
|
|
37
|
+
var elements = typescript_1.default.isObjectLiteralExpression(argument)
|
|
38
|
+
? argument.properties
|
|
39
|
+
: typescript_1.default.isArrayLiteralExpression(argument)
|
|
40
|
+
? argument.elements
|
|
41
|
+
: undefined;
|
|
42
|
+
var parameterDiagnostic = diagnostics_1.Diagnostics.createDiagnostic((_c = parameter.valueDeclaration) !== null && _c !== void 0 ? _c : argument, typescript_1.default.DiagnosticCategory.Message, "Required because this parameter must be known at compile-time.");
|
|
43
|
+
// This argument is not an object or array literal.
|
|
44
|
+
if (!elements) {
|
|
45
|
+
var baseDiagnostic = diagnostics_1.Diagnostics.createDiagnostic(argument, typescript_1.default.DiagnosticCategory.Error, "Flamework expected this argument to be a literal expression.");
|
|
46
|
+
typescript_1.default.addRelatedInfo(baseDiagnostic, parameterDiagnostic);
|
|
47
|
+
throw new diagnostics_1.DiagnosticError(baseDiagnostic);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
// We also want to validate that there are no spread operations inside the literal.
|
|
51
|
+
for (var elements_1 = (e_2 = void 0, __values(elements)), elements_1_1 = elements_1.next(); !elements_1_1.done; elements_1_1 = elements_1.next()) {
|
|
52
|
+
var element = elements_1_1.value;
|
|
53
|
+
if (typescript_1.default.isSpreadElement(element) || typescript_1.default.isSpreadAssignment(element)) {
|
|
54
|
+
var baseDiagnostic = diagnostics_1.Diagnostics.createDiagnostic(element, typescript_1.default.DiagnosticCategory.Error, "Flamework does not support spread expressions in this location.");
|
|
55
|
+
typescript_1.default.addRelatedInfo(baseDiagnostic, parameterDiagnostic);
|
|
56
|
+
throw new diagnostics_1.DiagnosticError(baseDiagnostic);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
61
|
+
finally {
|
|
62
|
+
try {
|
|
63
|
+
if (elements_1_1 && !elements_1_1.done && (_d = elements_1.return)) _d.call(elements_1);
|
|
64
|
+
}
|
|
65
|
+
finally { if (e_2) throw e_2.error; }
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
try {
|
|
69
|
+
for (var parameters_1 = __values(parameters), parameters_1_1 = parameters_1.next(); !parameters_1_1.done; parameters_1_1 = parameters_1.next()) {
|
|
70
|
+
var parameter = parameters_1_1.value;
|
|
71
|
+
_loop_1(parameter);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
75
|
+
finally {
|
|
76
|
+
try {
|
|
77
|
+
if (parameters_1_1 && !parameters_1_1.done && (_a = parameters_1.return)) _a.call(parameters_1);
|
|
78
|
+
}
|
|
79
|
+
finally { if (e_1) throw e_1.error; }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildPathGlobIntrinsic = buildPathGlobIntrinsic;
|
|
7
|
+
exports.buildPathIntrinsic = buildPathIntrinsic;
|
|
8
|
+
var path_1 = __importDefault(require("path"));
|
|
9
|
+
var factory_1 = require("../../../util/factory");
|
|
10
|
+
var diagnostics_1 = require("../../../classes/diagnostics");
|
|
11
|
+
/**
|
|
12
|
+
* Generates a path glob.
|
|
13
|
+
*
|
|
14
|
+
* This generates a string as a reference to the runtime metadata exposed in core.
|
|
15
|
+
*/
|
|
16
|
+
function buildPathGlobIntrinsic(state, node, pathType) {
|
|
17
|
+
if (!pathType.isStringLiteral()) {
|
|
18
|
+
diagnostics_1.Diagnostics.error(node, "Path is invalid, expected string literal and got: ".concat(state.typeChecker.typeToString(pathType)));
|
|
19
|
+
}
|
|
20
|
+
var file = state.getSourceFile(node);
|
|
21
|
+
var glob = pathType.value;
|
|
22
|
+
var absoluteGlob = glob.startsWith(".")
|
|
23
|
+
? path_1.default.relative(state.rootDirectory, path_1.default.resolve(path_1.default.dirname(file.fileName), glob)).replace(/\\/g, "/")
|
|
24
|
+
: glob;
|
|
25
|
+
state.buildInfo.addGlob(absoluteGlob, state.getFileId(file));
|
|
26
|
+
return factory_1.f.string(state.obfuscateText(absoluteGlob, "addPaths"));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Generates a path as an array of Rojo path segments.
|
|
30
|
+
*/
|
|
31
|
+
function buildPathIntrinsic(state, node, pathType) {
|
|
32
|
+
var _a;
|
|
33
|
+
if (!pathType.isStringLiteral()) {
|
|
34
|
+
diagnostics_1.Diagnostics.error(node, "Path is invalid, expected string literal and got: ".concat(state.typeChecker.typeToString(pathType)));
|
|
35
|
+
}
|
|
36
|
+
var outputPath = state.pathTranslator.getOutputPath(pathType.value);
|
|
37
|
+
var rbxPath = (_a = state.rojoResolver) === null || _a === void 0 ? void 0 : _a.getRbxPathFromFilePath(outputPath);
|
|
38
|
+
if (!rbxPath) {
|
|
39
|
+
diagnostics_1.Diagnostics.error(node, "Could not find Rojo data for '".concat(pathType.value, "'"));
|
|
40
|
+
}
|
|
41
|
+
return factory_1.f.array(rbxPath.map(factory_1.f.string));
|
|
42
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __values = (this && this.__values) || function(o) {
|
|
3
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
4
|
+
if (m) return m.call(o);
|
|
5
|
+
if (o && typeof o.length === "number") return {
|
|
6
|
+
next: function () {
|
|
7
|
+
if (o && i >= o.length) o = void 0;
|
|
8
|
+
return { value: o && o[i++], done: !o };
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
|
+
};
|
|
13
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
14
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
15
|
+
if (!m) return o;
|
|
16
|
+
var i = m.call(o), r, ar = [], e;
|
|
17
|
+
try {
|
|
18
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
19
|
+
}
|
|
20
|
+
catch (error) { e = { error: error }; }
|
|
21
|
+
finally {
|
|
22
|
+
try {
|
|
23
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
24
|
+
}
|
|
25
|
+
finally { if (e) throw e.error; }
|
|
26
|
+
}
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
30
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
31
|
+
if (ar || !(i in from)) {
|
|
32
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
33
|
+
ar[i] = from[i];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
37
|
+
};
|
|
38
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
|
+
};
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.updateComponentConfig = updateComponentConfig;
|
|
43
|
+
var typescript_1 = __importDefault(require("typescript"));
|
|
44
|
+
var buildGuardFromType_1 = require("../../util/functions/buildGuardFromType");
|
|
45
|
+
var factory_1 = require("../../util/factory");
|
|
46
|
+
var getSuperClasses_1 = require("../../util/functions/getSuperClasses");
|
|
47
|
+
var nodeMetadata_1 = require("../../classes/nodeMetadata");
|
|
48
|
+
var diagnosticsUtils_1 = require("../../util/diagnosticsUtils");
|
|
49
|
+
var getInstanceTypeFromType_1 = require("../../util/functions/getInstanceTypeFromType");
|
|
50
|
+
var diagnostics_1 = require("../../classes/diagnostics");
|
|
51
|
+
var uid_1 = require("../../util/uid");
|
|
52
|
+
var buildInstanceShape_1 = require("../../util/functions/buildInstanceShape");
|
|
53
|
+
/**
|
|
54
|
+
* The property every component carries, which is how a component type is told apart from the
|
|
55
|
+
* Instance types around it.
|
|
56
|
+
*/
|
|
57
|
+
var COMPONENT_BRAND = "_flamework_link_instance";
|
|
58
|
+
/**
|
|
59
|
+
* Reads one of `BaseComponent`'s type parameters through the property that carries it, so that a
|
|
60
|
+
* subclass gets the instantiated type and an unrelated class gets nothing.
|
|
61
|
+
*/
|
|
62
|
+
function getMarkedType(state, node, property, marker) {
|
|
63
|
+
var type = state.typeChecker.getTypeAtLocation(node);
|
|
64
|
+
var symbol = type.getProperty(property);
|
|
65
|
+
if (!symbol)
|
|
66
|
+
return;
|
|
67
|
+
var metadata = nodeMetadata_1.NodeMetadata.fromSymbol(state, symbol);
|
|
68
|
+
if (!metadata || !metadata.isRequested(marker))
|
|
69
|
+
return;
|
|
70
|
+
return state.typeChecker.getTypeOfSymbolAtLocation(symbol, node);
|
|
71
|
+
}
|
|
72
|
+
function calculateOmittedGuards(state, classDeclaration, customAttributes) {
|
|
73
|
+
var e_1, _a, e_2, _b;
|
|
74
|
+
var omittedNames = new Set();
|
|
75
|
+
if (factory_1.f.is.propertyAssignmentDeclaration(customAttributes) && factory_1.f.is.object(customAttributes.initializer)) {
|
|
76
|
+
try {
|
|
77
|
+
for (var _c = __values(customAttributes.initializer.properties), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
78
|
+
var prop = _d.value;
|
|
79
|
+
if (factory_1.f.is.string(prop.name) || factory_1.f.is.identifier(prop.name)) {
|
|
80
|
+
omittedNames.add(prop.name.text);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
85
|
+
finally {
|
|
86
|
+
try {
|
|
87
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
88
|
+
}
|
|
89
|
+
finally { if (e_1) throw e_1.error; }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
var type = state.typeChecker.getTypeAtLocation(classDeclaration);
|
|
93
|
+
var property = type.getProperty("_flamework_attribute_guards");
|
|
94
|
+
if (!property)
|
|
95
|
+
return omittedNames;
|
|
96
|
+
var superClass = (0, getSuperClasses_1.getSuperClasses)(state.typeChecker, classDeclaration)[0];
|
|
97
|
+
if (!superClass)
|
|
98
|
+
return omittedNames;
|
|
99
|
+
var superType = state.typeChecker.getTypeAtLocation(superClass);
|
|
100
|
+
var superProperty = superType.getProperty("_flamework_attribute_guards");
|
|
101
|
+
if (!superProperty)
|
|
102
|
+
return omittedNames;
|
|
103
|
+
var attributes = state.typeChecker.getTypeOfSymbolAtLocation(property, classDeclaration);
|
|
104
|
+
var superAttributes = state.typeChecker.getTypeOfSymbolAtLocation(superProperty, superClass);
|
|
105
|
+
try {
|
|
106
|
+
for (var _e = __values(superAttributes.getProperties()), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
107
|
+
var name_1 = _f.value.name;
|
|
108
|
+
var prop = state.typeChecker.getTypeOfPropertyOfType(attributes, name_1);
|
|
109
|
+
var superProp = state.typeChecker.getTypeOfPropertyOfType(superAttributes, name_1);
|
|
110
|
+
if (prop && superProp && superProp === prop) {
|
|
111
|
+
omittedNames.add(name_1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
|
116
|
+
finally {
|
|
117
|
+
try {
|
|
118
|
+
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
|
119
|
+
}
|
|
120
|
+
finally { if (e_2) throw e_2.error; }
|
|
121
|
+
}
|
|
122
|
+
return omittedNames;
|
|
123
|
+
}
|
|
124
|
+
function updateAttributeGuards(state, node, properties) {
|
|
125
|
+
var _a;
|
|
126
|
+
// The guards come from the written shape of the attributes rather than the declared one: an
|
|
127
|
+
// instance-valued attribute is stored as an `InstanceHandle`, so that is what is checked.
|
|
128
|
+
var attributesType = getMarkedType(state, node, "_flamework_attribute_guards", "intrinsic-component-attribute-guards");
|
|
129
|
+
if (!attributesType)
|
|
130
|
+
return;
|
|
131
|
+
var attributes = properties.find(function (x) { return x.name && "text" in x.name && x.name.text === "attributes"; });
|
|
132
|
+
var attributeGuards = (0, diagnosticsUtils_1.withDiagnosticContext)((_a = node.name) !== null && _a !== void 0 ? _a : node, function () { return "Failed to generate component attributes: ".concat(state.typeChecker.typeToString(attributesType)); }, function () { var _a; return (0, buildGuardFromType_1.buildGuardsFromType)(state, (_a = node.name) !== null && _a !== void 0 ? _a : node, attributesType); });
|
|
133
|
+
var omittedGuards = calculateOmittedGuards(state, node, attributes);
|
|
134
|
+
var filteredGuards = attributeGuards.filter(function (x) { return !omittedGuards.has(x.name.text); });
|
|
135
|
+
properties = properties.filter(function (x) { return x !== attributes; });
|
|
136
|
+
if (factory_1.f.is.propertyAssignmentDeclaration(attributes) && factory_1.f.is.object(attributes.initializer)) {
|
|
137
|
+
properties.push(factory_1.f.update.propertyAssignmentDeclaration(attributes, factory_1.f.update.object(attributes.initializer, __spreadArray(__spreadArray([], __read(attributes.initializer.properties.map(function (v) { return state.transformNode(v); })), false), __read(filteredGuards), false)), attributes.name));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
properties.push(factory_1.f.propertyAssignmentDeclaration("attributes", factory_1.f.object(filteredGuards)));
|
|
141
|
+
}
|
|
142
|
+
return properties;
|
|
143
|
+
}
|
|
144
|
+
function updateInstanceGuard(state, node, properties) {
|
|
145
|
+
var type = state.typeChecker.getTypeAtLocation(node);
|
|
146
|
+
var property = type.getProperty("instance");
|
|
147
|
+
if (!property)
|
|
148
|
+
return;
|
|
149
|
+
var attributesMeta = nodeMetadata_1.NodeMetadata.fromSymbol(state, property);
|
|
150
|
+
if (!attributesMeta || !attributesMeta.isRequested("intrinsic-component-instance"))
|
|
151
|
+
return;
|
|
152
|
+
var superClass = (0, getSuperClasses_1.getSuperClasses)(state.typeChecker, node)[0];
|
|
153
|
+
if (!superClass)
|
|
154
|
+
return;
|
|
155
|
+
var customGuard = properties.find(function (x) { return x.name && "text" in x.name && x.name.text === "instanceGuard"; });
|
|
156
|
+
if (customGuard)
|
|
157
|
+
return;
|
|
158
|
+
var instanceType = state.typeChecker.getTypeOfSymbolAtLocation(property, node);
|
|
159
|
+
if (!instanceType)
|
|
160
|
+
return;
|
|
161
|
+
var superType = state.typeChecker.getTypeAtLocation(superClass);
|
|
162
|
+
var superProperty = superType.getProperty("instance");
|
|
163
|
+
if (!superProperty)
|
|
164
|
+
return;
|
|
165
|
+
var superInstanceType = state.typeChecker.getTypeOfSymbolAtLocation(superProperty, superClass);
|
|
166
|
+
if (!superInstanceType)
|
|
167
|
+
return;
|
|
168
|
+
if (!type.checker.isTypeAssignableTo(superInstanceType, instanceType)) {
|
|
169
|
+
// As data wherever the type is only classes and children, which the runtime watches one
|
|
170
|
+
// child at a time and can explain; as a guard otherwise. A child naming a component has a
|
|
171
|
+
// tree of its own that is that component's business, so the shape stops at its class.
|
|
172
|
+
var shape = (0, buildInstanceShape_1.buildInstanceShape)(state, node, instanceType, getComponentChildNames(state, node));
|
|
173
|
+
if (shape !== undefined) {
|
|
174
|
+
properties.push(factory_1.f.propertyAssignmentDeclaration("instanceShape", shape));
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
properties.push(factory_1.f.propertyAssignmentDeclaration("instanceGuard", (0, buildGuardFromType_1.buildGuardFromType)(state, node, instanceType)));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return properties;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The instance a component type is attached to, or nothing when the type is not a component. This
|
|
184
|
+
* is the resolved tree rather than the declared one, so a linked component's own children are part
|
|
185
|
+
* of the guard.
|
|
186
|
+
*/
|
|
187
|
+
function getComponentInstanceType(state, type, node) {
|
|
188
|
+
if (!type.getProperty(COMPONENT_BRAND))
|
|
189
|
+
return;
|
|
190
|
+
var instance = type.getProperty("instance");
|
|
191
|
+
if (!instance)
|
|
192
|
+
return;
|
|
193
|
+
return state.typeChecker.getTypeOfSymbolAtLocation(instance, node);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The members of an instance type that are not part of the Roblox class itself, which is how the
|
|
197
|
+
* guard builder reads an intersection: as the children the instance must have.
|
|
198
|
+
*/
|
|
199
|
+
function getDeclaredChildren(state, node, type) {
|
|
200
|
+
var e_3, _a;
|
|
201
|
+
var instanceType = (0, getInstanceTypeFromType_1.getInstanceTypeFromType)(node.getSourceFile(), type);
|
|
202
|
+
var children = new Array();
|
|
203
|
+
try {
|
|
204
|
+
for (var _b = __values(type.getProperties()), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
205
|
+
var property = _c.value;
|
|
206
|
+
if (instanceType.getProperty(property.name))
|
|
207
|
+
continue;
|
|
208
|
+
var propertyType = state.typeChecker.getTypeOfSymbolAtLocation(property, node);
|
|
209
|
+
if (propertyType)
|
|
210
|
+
children.push([property, propertyType]);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (e_3_1) { e_3 = { error: e_3_1 }; }
|
|
214
|
+
finally {
|
|
215
|
+
try {
|
|
216
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
217
|
+
}
|
|
218
|
+
finally { if (e_3) throw e_3.error; }
|
|
219
|
+
}
|
|
220
|
+
return children;
|
|
221
|
+
}
|
|
222
|
+
function isOptionalMember(state, symbol, type) {
|
|
223
|
+
return (symbol.flags & typescript_1.default.SymbolFlags.Optional) !== 0 || state.typeChecker.getNonNullableType(type) !== type;
|
|
224
|
+
}
|
|
225
|
+
function createLink(state, node, kind, name, optional, componentType, guardType) {
|
|
226
|
+
var _a, _b, _c;
|
|
227
|
+
var fields = [
|
|
228
|
+
factory_1.f.propertyAssignmentDeclaration("kind", kind),
|
|
229
|
+
factory_1.f.propertyAssignmentDeclaration("name", name),
|
|
230
|
+
factory_1.f.propertyAssignmentDeclaration("optional", optional),
|
|
231
|
+
];
|
|
232
|
+
if (guardType) {
|
|
233
|
+
var shape = (0, buildInstanceShape_1.buildInstanceShape)(state, (_a = node.name) !== null && _a !== void 0 ? _a : node, guardType);
|
|
234
|
+
fields.push(shape !== undefined
|
|
235
|
+
? factory_1.f.propertyAssignmentDeclaration("shape", shape)
|
|
236
|
+
: factory_1.f.propertyAssignmentDeclaration("guard", (0, diagnosticsUtils_1.withDiagnosticContext)((_b = node.name) !== null && _b !== void 0 ? _b : node, function () { return "Failed to generate a guard for the '".concat(name, "' link"); }, function () { var _a; return (0, buildGuardFromType_1.buildGuardFromType)(state, (_a = node.name) !== null && _a !== void 0 ? _a : node, guardType); })));
|
|
237
|
+
}
|
|
238
|
+
if (componentType) {
|
|
239
|
+
fields.push(factory_1.f.propertyAssignmentDeclaration("component", (0, uid_1.getTypeUid)(state, componentType, (_c = node.name) !== null && _c !== void 0 ? _c : node)));
|
|
240
|
+
}
|
|
241
|
+
return factory_1.f.object(fields, false);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* The direct children of the declared tree that name a component. Their trees belong to that
|
|
245
|
+
* component -- its tracker checks and watches them -- so the owner's shape stops at their class.
|
|
246
|
+
*/
|
|
247
|
+
function getComponentChildNames(state, node) {
|
|
248
|
+
var e_4, _a;
|
|
249
|
+
var names = new Set();
|
|
250
|
+
var instanceType = getMarkedType(state, node, COMPONENT_BRAND, "intrinsic-component-instance-links");
|
|
251
|
+
if (!instanceType)
|
|
252
|
+
return names;
|
|
253
|
+
try {
|
|
254
|
+
for (var _b = __values(getDeclaredChildren(state, node, instanceType)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
255
|
+
var _d = __read(_c.value, 2), property = _d[0], declaredType = _d[1];
|
|
256
|
+
var targetType = state.typeChecker.getNonNullableType(declaredType);
|
|
257
|
+
if (getComponentInstanceType(state, targetType, node))
|
|
258
|
+
names.add(property.name);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (e_4_1) { e_4 = { error: e_4_1 }; }
|
|
262
|
+
finally {
|
|
263
|
+
try {
|
|
264
|
+
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
265
|
+
}
|
|
266
|
+
finally { if (e_4) throw e_4.error; }
|
|
267
|
+
}
|
|
268
|
+
return names;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Discovers the components and instances a component links to, from the attributes it declares and
|
|
272
|
+
* from its instance tree. `Components` waits for each one and keeps it resolved.
|
|
273
|
+
*/
|
|
274
|
+
function updateLinks(state, node, properties) {
|
|
275
|
+
var e_5, _a, e_6, _b;
|
|
276
|
+
var attributesType = getMarkedType(state, node, "_flamework_link_attributes", "intrinsic-component-attribute-links");
|
|
277
|
+
var instanceType = getMarkedType(state, node, COMPONENT_BRAND, "intrinsic-component-instance-links");
|
|
278
|
+
if (!attributesType && !instanceType)
|
|
279
|
+
return;
|
|
280
|
+
var links = new Array();
|
|
281
|
+
if (attributesType) {
|
|
282
|
+
try {
|
|
283
|
+
for (var _c = __values(attributesType.getProperties()), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
284
|
+
var property = _d.value;
|
|
285
|
+
var declaredType = state.typeChecker.getTypeOfSymbolAtLocation(property, node);
|
|
286
|
+
var targetType = state.typeChecker.getNonNullableType(declaredType);
|
|
287
|
+
var optional = isOptionalMember(state, property, declaredType);
|
|
288
|
+
if (getComponentInstanceType(state, targetType, node)) {
|
|
289
|
+
// No guard or shape of its own: the instance a component link names has to carry
|
|
290
|
+
// that component, and that component's tracker is what checks its tree.
|
|
291
|
+
links.push(createLink(state, node, "attribute", property.name, optional, targetType));
|
|
292
|
+
}
|
|
293
|
+
else if ((0, buildGuardFromType_1.isInstanceType)(targetType)) {
|
|
294
|
+
links.push(createLink(state, node, "attribute", property.name, optional, undefined, targetType));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (e_5_1) { e_5 = { error: e_5_1 }; }
|
|
299
|
+
finally {
|
|
300
|
+
try {
|
|
301
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
302
|
+
}
|
|
303
|
+
finally { if (e_5) throw e_5.error; }
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (instanceType) {
|
|
307
|
+
try {
|
|
308
|
+
for (var _e = __values(getDeclaredChildren(state, node, instanceType)), _f = _e.next(); !_f.done; _f = _e.next()) {
|
|
309
|
+
var _g = __read(_f.value, 2), property = _g[0], declaredType = _g[1];
|
|
310
|
+
var targetType = state.typeChecker.getNonNullableType(declaredType);
|
|
311
|
+
var optional = isOptionalMember(state, property, declaredType);
|
|
312
|
+
if (getComponentInstanceType(state, targetType, node)) {
|
|
313
|
+
// A child's own guard is part of the component's instance guard, so the link only
|
|
314
|
+
// has to name the component that must exist on it.
|
|
315
|
+
links.push(createLink(state, node, "child", property.name, optional, targetType));
|
|
316
|
+
}
|
|
317
|
+
else if ((0, buildGuardFromType_1.isInstanceType)(targetType)) {
|
|
318
|
+
if (optional)
|
|
319
|
+
assertChildIsRequired(node, property, property.name);
|
|
320
|
+
assertInstanceTree(state, node, targetType, property.name);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch (e_6_1) { e_6 = { error: e_6_1 }; }
|
|
325
|
+
finally {
|
|
326
|
+
try {
|
|
327
|
+
if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
|
|
328
|
+
}
|
|
329
|
+
finally { if (e_6) throw e_6.error; }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (links.length !== 0) {
|
|
333
|
+
properties.push(factory_1.f.propertyAssignmentDeclaration("links", factory_1.f.array(links)));
|
|
334
|
+
}
|
|
335
|
+
return properties;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* The node a diagnostic about a declared child belongs on, which is the child itself wherever it
|
|
339
|
+
* was written down, and the component otherwise.
|
|
340
|
+
*/
|
|
341
|
+
function getPropertyNode(node, property) {
|
|
342
|
+
var _a, _b, _c;
|
|
343
|
+
var declaration = (_a = property.valueDeclaration) !== null && _a !== void 0 ? _a : (_b = property.declarations) === null || _b === void 0 ? void 0 : _b[0];
|
|
344
|
+
if (!declaration)
|
|
345
|
+
return (_c = node.name) !== null && _c !== void 0 ? _c : node;
|
|
346
|
+
return factory_1.f.is.namedDeclaration(declaration) ? declaration.name : declaration;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* A child of the instance tree cannot be optional. `this.instance.Head` is an index into the
|
|
350
|
+
* instance itself, and Roblox raises on a child that is not there rather than handing back nothing,
|
|
351
|
+
* so the optional type would promise a read that is not safe to make.
|
|
352
|
+
*
|
|
353
|
+
* A child typed as a *component* is the exception, and is let through above: it is a link, so its
|
|
354
|
+
* presence is watched and `childComponents.Head` -- an ordinary table read -- is what says whether
|
|
355
|
+
* the child is there.
|
|
356
|
+
*/
|
|
357
|
+
function assertChildIsRequired(node, property, path) {
|
|
358
|
+
var component = node.name ? " of '".concat(node.name.text, "'") : "";
|
|
359
|
+
// Naming a component is only an option for a direct child; one deeper in the tree cannot be
|
|
360
|
+
// linked at all, so it is not offered as a way out there.
|
|
361
|
+
var link = path.includes(".")
|
|
362
|
+
? ""
|
|
363
|
+
: " type it as a component so that Flamework watches it and 'childComponents' says whether it is there, or";
|
|
364
|
+
diagnostics_1.Diagnostics.error(getPropertyNode(node, property), "Child '".concat(path, "' of the instance tree").concat(component, " is optional, which Flamework does not allow: Roblox raises when a child that does not exist is indexed, so 'this.instance.").concat(path, "' would error rather than be undefined."), "Require the child,".concat(link, " leave it out of the tree and reach for it with FindFirstChild."));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Checks the children declared below a direct child of the instance tree.
|
|
368
|
+
*
|
|
369
|
+
* A component deeper in the tree cannot be linked: `this.instance` only resolves components it
|
|
370
|
+
* holds directly, and the guard builder would meet the class itself. Saying so here beats the
|
|
371
|
+
* "Flamework does not support generating guards for classes" that would follow. An optional child
|
|
372
|
+
* is refused at every depth, for the same reason it is refused at the top.
|
|
373
|
+
*/
|
|
374
|
+
function assertInstanceTree(state, node, type, path) {
|
|
375
|
+
var e_7, _a;
|
|
376
|
+
var _b;
|
|
377
|
+
try {
|
|
378
|
+
for (var _c = __values(getDeclaredChildren(state, node, type)), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
379
|
+
var _e = __read(_d.value, 2), property = _e[0], declaredType = _e[1];
|
|
380
|
+
var targetType = state.typeChecker.getNonNullableType(declaredType);
|
|
381
|
+
var childPath = "".concat(path, ".").concat(property.name);
|
|
382
|
+
if (targetType.getProperty(COMPONENT_BRAND)) {
|
|
383
|
+
diagnostics_1.Diagnostics.error((_b = node.name) !== null && _b !== void 0 ? _b : node, "Component '".concat(state.typeChecker.typeToString(targetType), "' is linked at '").concat(childPath, "', which is not a direct child of this component."), "Only a direct child of the instance tree can name a component. Declare it on the component attached to that child, or look it up with getComponent.");
|
|
384
|
+
}
|
|
385
|
+
if ((0, buildGuardFromType_1.isInstanceType)(targetType)) {
|
|
386
|
+
if (isOptionalMember(state, property, declaredType)) {
|
|
387
|
+
assertChildIsRequired(node, property, childPath);
|
|
388
|
+
}
|
|
389
|
+
assertInstanceTree(state, node, targetType, childPath);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
catch (e_7_1) { e_7 = { error: e_7_1 }; }
|
|
394
|
+
finally {
|
|
395
|
+
try {
|
|
396
|
+
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
397
|
+
}
|
|
398
|
+
finally { if (e_7) throw e_7.error; }
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function updateComponentConfig(state, node, properties) {
|
|
402
|
+
var _a, _b, _c;
|
|
403
|
+
// Links first: they are what reports a component the guards cannot be generated for, and that
|
|
404
|
+
// reads far better than the "cannot generate a guard for a class" the guards would raise.
|
|
405
|
+
properties = (_a = updateLinks(state, node, properties)) !== null && _a !== void 0 ? _a : properties;
|
|
406
|
+
properties = (_b = updateAttributeGuards(state, node, properties)) !== null && _b !== void 0 ? _b : properties;
|
|
407
|
+
properties = (_c = updateInstanceGuard(state, node, properties)) !== null && _c !== void 0 ? _c : properties;
|
|
408
|
+
return properties;
|
|
409
|
+
}
|