typia 15.1.0 → 15.2.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/lib/TypeGuardError.js.map +1 -1
- package/lib/basic.js.map +1 -1
- package/lib/compare.js.map +1 -1
- package/lib/functional.js.map +1 -1
- package/lib/http.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/internal/_IProtobufWriter.js.map +1 -1
- package/lib/internal/_ProtobufReader.js.map +1 -1
- package/lib/internal/_ProtobufSizer.js.map +1 -1
- package/lib/internal/_ProtobufWriter.js.map +1 -1
- package/lib/internal/_accessExpressionAsString.js.map +1 -1
- package/lib/internal/_assertGuard.js.map +1 -1
- package/lib/internal/_coerceLlmArguments.js.map +1 -1
- package/lib/internal/_createLlmEvaluation.js.map +1 -1
- package/lib/internal/_createStandardSchema.js.map +1 -1
- package/lib/internal/_decimal.js.map +1 -1
- package/lib/internal/_functionalTypeGuardErrorFactory.js.map +1 -1
- package/lib/internal/_httpFormDataReadArray.js.map +1 -1
- package/lib/internal/_httpFormDataReadBigint.js.map +1 -1
- package/lib/internal/_httpFormDataReadBlob.js.map +1 -1
- package/lib/internal/_httpFormDataReadBoolean.js.map +1 -1
- package/lib/internal/_httpFormDataReadFile.js.map +1 -1
- package/lib/internal/_httpFormDataReadNumber.js.map +1 -1
- package/lib/internal/_httpFormDataReadString.js.map +1 -1
- package/lib/internal/_httpHeaderReadBigint.js.map +1 -1
- package/lib/internal/_httpHeaderReadBoolean.js.map +1 -1
- package/lib/internal/_httpHeaderReadNumber.js.map +1 -1
- package/lib/internal/_httpParameterReadBigint.js.map +1 -1
- package/lib/internal/_httpParameterReadBoolean.js.map +1 -1
- package/lib/internal/_httpParameterReadNumber.js.map +1 -1
- package/lib/internal/_httpParameterReadString.js.map +1 -1
- package/lib/internal/_httpQueryParseURLSearchParams.js.map +1 -1
- package/lib/internal/_httpQueryReadArray.js.map +1 -1
- package/lib/internal/_httpQueryReadBigint.js.map +1 -1
- package/lib/internal/_httpQueryReadBoolean.js.map +1 -1
- package/lib/internal/_httpQueryReadNumber.js.map +1 -1
- package/lib/internal/_httpQueryReadString.js.map +1 -1
- package/lib/internal/_isBetween.js.map +1 -1
- package/lib/internal/_isBigintString.js.map +1 -1
- package/lib/internal/_isFormatByte.js.map +1 -1
- package/lib/internal/_isFormatDate.js.map +1 -1
- package/lib/internal/_isFormatDateTime.js.map +1 -1
- package/lib/internal/_isFormatDuration.js.map +1 -1
- package/lib/internal/_isFormatEmail.js.map +1 -1
- package/lib/internal/_isFormatHostname.js.map +1 -1
- package/lib/internal/_isFormatIdnEmail.js.map +1 -1
- package/lib/internal/_isFormatIdnHostname.js.map +1 -1
- package/lib/internal/_isFormatIpv4.js.map +1 -1
- package/lib/internal/_isFormatIpv6.js.map +1 -1
- package/lib/internal/_isFormatIri.js.map +1 -1
- package/lib/internal/_isFormatIriReference.js.map +1 -1
- package/lib/internal/_isFormatJsonPointer.js.map +1 -1
- package/lib/internal/_isFormatPassword.js.map +1 -1
- package/lib/internal/_isFormatRegex.js.map +1 -1
- package/lib/internal/_isFormatRelativeJsonPointer.js.map +1 -1
- package/lib/internal/_isFormatTime.js.map +1 -1
- package/lib/internal/_isFormatUri.js.map +1 -1
- package/lib/internal/_isFormatUriReference.js.map +1 -1
- package/lib/internal/_isFormatUriTemplate.js.map +1 -1
- package/lib/internal/_isFormatUrl.js.map +1 -1
- package/lib/internal/_isFormatUuid.js.map +1 -1
- package/lib/internal/_isMultipleOf.js.map +1 -1
- package/lib/internal/_isTypeFloat.js.map +1 -1
- package/lib/internal/_isTypeInt16.js.map +1 -1
- package/lib/internal/_isTypeInt32.js.map +1 -1
- package/lib/internal/_isTypeInt64.js.map +1 -1
- package/lib/internal/_isTypeInt64Bigint.js.map +1 -1
- package/lib/internal/_isTypeInt8.js.map +1 -1
- package/lib/internal/_isTypeUint16.js.map +1 -1
- package/lib/internal/_isTypeUint32.js.map +1 -1
- package/lib/internal/_isTypeUint64.js.map +1 -1
- package/lib/internal/_isTypeUint64Bigint.js.map +1 -1
- package/lib/internal/_isTypeUint8.js.map +1 -1
- package/lib/internal/_isUniqueItems.js.map +1 -1
- package/lib/internal/_jsonStringifyArray.js.map +1 -1
- package/lib/internal/_jsonStringifyElement.js.map +1 -1
- package/lib/internal/_jsonStringifyNumber.js.map +1 -1
- package/lib/internal/_jsonStringifyProperty.js.map +1 -1
- package/lib/internal/_jsonStringifyRest.js.map +1 -1
- package/lib/internal/_jsonStringifyString.js.map +1 -1
- package/lib/internal/_jsonStringifyTail.js.map +1 -1
- package/lib/internal/_llmApplicationFinalize.js +8 -1
- package/lib/internal/_llmApplicationFinalize.js.map +1 -1
- package/lib/internal/_notationAny.js.map +1 -1
- package/lib/internal/_notationAssign.js.map +1 -1
- package/lib/internal/_notationCamel.js.map +1 -1
- package/lib/internal/_notationKebab.js.map +1 -1
- package/lib/internal/_notationKeyCollision.js.map +1 -1
- package/lib/internal/_notationPascal.js.map +1 -1
- package/lib/internal/_notationSnake.js.map +1 -1
- package/lib/internal/_parseLlmArguments.js.map +1 -1
- package/lib/internal/_plainCloneAny.js.map +1 -1
- package/lib/internal/_randomArray.js.map +1 -1
- package/lib/internal/_randomBigint.js.map +1 -1
- package/lib/internal/_randomBoolean.js.map +1 -1
- package/lib/internal/_randomFormatByte.js.map +1 -1
- package/lib/internal/_randomFormatDate.js.map +1 -1
- package/lib/internal/_randomFormatDatetime.js.map +1 -1
- package/lib/internal/_randomFormatDuration.js.map +1 -1
- package/lib/internal/_randomFormatEmail.js.map +1 -1
- package/lib/internal/_randomFormatHostname.js.map +1 -1
- package/lib/internal/_randomFormatIdnEmail.js.map +1 -1
- package/lib/internal/_randomFormatIdnHostname.js.map +1 -1
- package/lib/internal/_randomFormatIpv4.js.map +1 -1
- package/lib/internal/_randomFormatIpv6.js.map +1 -1
- package/lib/internal/_randomFormatIri.js.map +1 -1
- package/lib/internal/_randomFormatIriReference.js.map +1 -1
- package/lib/internal/_randomFormatJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatPassword.js.map +1 -1
- package/lib/internal/_randomFormatRegex.js.map +1 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatTime.js.map +1 -1
- package/lib/internal/_randomFormatUri.js.map +1 -1
- package/lib/internal/_randomFormatUriReference.js.map +1 -1
- package/lib/internal/_randomFormatUriTemplate.js.map +1 -1
- package/lib/internal/_randomFormatUrl.js.map +1 -1
- package/lib/internal/_randomFormatUuid.js.map +1 -1
- package/lib/internal/_randomInteger.js.map +1 -1
- package/lib/internal/_randomMultiple.js.map +1 -1
- package/lib/internal/_randomNumber.js.map +1 -1
- package/lib/internal/_randomPattern.js.map +1 -1
- package/lib/internal/_randomPick.js.map +1 -1
- package/lib/internal/_randomString.js.map +1 -1
- package/lib/internal/_randomStringLength.js.map +1 -1
- package/lib/internal/_stringLength.js.map +1 -1
- package/lib/internal/_stringLengthGte.js.map +1 -1
- package/lib/internal/_stringLengthLte.js.map +1 -1
- package/lib/internal/_throwTypeGuardError.js.map +1 -1
- package/lib/internal/_validateReport.js.map +1 -1
- package/lib/internal/private/__notationRename.js.map +1 -1
- package/lib/internal/private/__randomComposition.js.map +1 -1
- package/lib/internal/private/__randomEpoch.js.map +1 -1
- package/lib/json.js.map +1 -1
- package/lib/llm.js.map +1 -1
- package/lib/module.js.map +1 -1
- package/lib/notations.js.map +1 -1
- package/lib/plain.js.map +1 -1
- package/lib/protobuf.js.map +1 -1
- package/lib/re-exports.js.map +1 -1
- package/lib/reflect.js.map +1 -1
- package/lib/transform.d.ts +5 -2
- package/lib/transform.js +6 -2
- package/lib/transform.js.map +1 -1
- package/lib/transform.mjs +7 -3
- package/lib/transform.mjs.map +1 -1
- package/lib/transformers/NoTransformConfigurationError.js.map +1 -1
- package/native/cmd/ttsc-typia/build.go +3 -1
- package/native/cmd/ttsc-typia/content_mapper_transform_ts71_test.go +102 -0
- package/native/cmd/ttsc-typia/diagnostics.go +1 -1
- package/native/cmd/ttsc-typia/distinct_programs_concurrent_emit_test.go +167 -0
- package/native/cmd/ttsc-typia/exact_optional_family_emission_contract_test.go +2 -1
- package/native/cmd/ttsc-typia/project_dependencies_heritage_forms_transform_test.go +84 -0
- package/native/cmd/ttsc-typia/schema_literal_helpers_test.go +2 -1
- package/native/cmd/ttsc-typia/transform.go +7 -6
- package/native/core/context/EmitFactory.go +20 -29
- package/native/core/context/ImportProgrammer.go +5 -7
- package/native/core/context/NodeFactory.go +25 -0
- package/native/core/context/node_factory_ownership_test.go +45 -0
- package/native/core/factories/ExpressionFactory.go +13 -14
- package/native/core/factories/IdentifierFactory.go +3 -5
- package/native/core/factories/LiteralFactory.go +6 -8
- package/native/core/factories/NumericRangeFactory.go +8 -10
- package/native/core/factories/StatementFactory.go +5 -7
- package/native/core/factories/TemplateFactory.go +1 -3
- package/native/core/factories/TypeFactory.go +7 -9
- package/native/core/factories/ValueFactory.go +5 -7
- package/native/core/factories/factory_utility_coverage_test.go +21 -20
- package/native/core/factories/internal/metadata/MetadataHelper.go +13 -13
- package/native/core/factories/internal/metadata/comment_tags_coverage_test.go +7 -16
- package/native/core/factories/internal/metadata/emplace_metadata_object.go +36 -27
- package/native/core/factories/internal/metadata/iterate_metadata_alias.go +2 -2
- package/native/core/factories/internal/metadata/iterate_metadata_constant.go +4 -4
- package/native/core/factories/internal/metadata/iterate_metadata_function.go +2 -2
- package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +11 -11
- package/native/core/factories/internal/metadata/js_doc_link_text_test.go +6 -16
- package/native/core/factories/internal/metadata/js_doc_parameter_name_reads_qualified_name_test.go +2 -1
- package/native/core/factories/internal/metadata/js_doc_source_helpers_test.go +44 -0
- package/native/core/factories/internal/metadata/js_doc_type_tag_blank_separation_test.go +16 -22
- package/native/core/factories/numeric_range_factory_enforces_64bit_bounds_test.go +2 -1
- package/native/core/programmers/AssertProgrammer.go +16 -18
- package/native/core/programmers/IsProgrammer.go +10 -12
- package/native/core/programmers/RandomProgrammer.go +29 -31
- package/native/core/programmers/ValidateProgrammer.go +20 -22
- package/native/core/programmers/compare/CompareEqualProgrammer.go +1 -3
- package/native/core/programmers/compare/CompareLessProgrammer.go +1 -3
- package/native/core/programmers/functional/FunctionalAssertFunctionProgrammer.go +3 -5
- package/native/core/programmers/functional/FunctionalAssertParametersProgrammer.go +2 -2
- package/native/core/programmers/functional/FunctionalAssertReturnProgrammer.go +2 -2
- package/native/core/programmers/functional/FunctionalIsFunctionProgrammer.go +2 -2
- package/native/core/programmers/functional/FunctionalIsParametersProgrammer.go +8 -10
- package/native/core/programmers/functional/FunctionalIsReturnProgrammer.go +2 -2
- package/native/core/programmers/functional/FunctionalValidateFunctionProgrammer.go +4 -6
- package/native/core/programmers/functional/FunctionalValidateParametersProgrammer.go +2 -2
- package/native/core/programmers/functional/FunctionalValidateReturnProgrammer.go +2 -2
- package/native/core/programmers/helpers/ClassifyJoiner.go +1 -3
- package/native/core/programmers/helpers/CloneJoiner.go +8 -14
- package/native/core/programmers/helpers/FunctionProgrammer.go +3 -5
- package/native/core/programmers/helpers/NotationJoiner.go +6 -12
- package/native/core/programmers/helpers/PruneJoiner.go +10 -12
- package/native/core/programmers/helpers/RandomJoiner.go +5 -7
- package/native/core/programmers/helpers/StringifyJoinder.go +15 -17
- package/native/core/programmers/helpers/UnionExplorer.go +24 -26
- package/native/core/programmers/http/HttpAssertFormDataProgrammer.go +2 -4
- package/native/core/programmers/http/HttpAssertHeadersProgrammer.go +1 -1
- package/native/core/programmers/http/HttpAssertQueryProgrammer.go +1 -1
- package/native/core/programmers/http/HttpFormDataProgrammer.go +9 -11
- package/native/core/programmers/http/HttpHeadersProgrammer.go +7 -9
- package/native/core/programmers/http/HttpIsFormDataProgrammer.go +2 -4
- package/native/core/programmers/http/HttpIsHeadersProgrammer.go +1 -1
- package/native/core/programmers/http/HttpIsQueryProgrammer.go +1 -1
- package/native/core/programmers/http/HttpParameterProgrammer.go +2 -4
- package/native/core/programmers/http/HttpQueryProgrammer.go +8 -10
- package/native/core/programmers/http/HttpValidateFormDataProgrammer.go +2 -4
- package/native/core/programmers/http/HttpValidateHeadersProgrammer.go +1 -1
- package/native/core/programmers/http/HttpValidateQueryProgrammer.go +1 -1
- package/native/core/programmers/internal/CheckerProgrammer.go +26 -28
- package/native/core/programmers/internal/FeatureProgrammer.go +32 -34
- package/native/core/programmers/iterate/check_array_length.go +1 -1
- package/native/core/programmers/iterate/check_bigint.go +2 -4
- package/native/core/programmers/iterate/check_dynamic_key.go +5 -7
- package/native/core/programmers/iterate/check_dynamic_properties.go +43 -57
- package/native/core/programmers/iterate/check_everything.go +3 -5
- package/native/core/programmers/iterate/check_exclude.go +2 -4
- package/native/core/programmers/iterate/check_native.go +2 -4
- package/native/core/programmers/iterate/check_number.go +2 -4
- package/native/core/programmers/iterate/check_object.go +3 -5
- package/native/core/programmers/iterate/check_object_type_tags.go +1 -1
- package/native/core/programmers/iterate/check_string.go +2 -4
- package/native/core/programmers/iterate/check_template.go +7 -9
- package/native/core/programmers/iterate/decode_union_object.go +2 -4
- package/native/core/programmers/iterate/feature_object_entries.go +2 -4
- package/native/core/programmers/json/JsonAssertParseProgrammer.go +2 -2
- package/native/core/programmers/json/JsonAssertStringifyProgrammer.go +2 -4
- package/native/core/programmers/json/JsonIsParseProgrammer.go +4 -6
- package/native/core/programmers/json/JsonIsStringifyProgrammer.go +2 -4
- package/native/core/programmers/json/JsonStringifyProgrammer.go +37 -39
- package/native/core/programmers/json/JsonValidateParseProgrammer.go +2 -2
- package/native/core/programmers/json/JsonValidateStringifyProgrammer.go +2 -4
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +1 -3
- package/native/core/programmers/llm/LlmCoerceProgrammer.go +2 -4
- package/native/core/programmers/llm/LlmControllerProgrammer.go +1 -3
- package/native/core/programmers/llm/LlmEvaluationProgrammer.go +1 -3
- package/native/core/programmers/llm/LlmParametersProgrammer.go +1 -3
- package/native/core/programmers/llm/LlmParseProgrammer.go +3 -5
- package/native/core/programmers/llm/LlmSchemaProgrammer.go +5 -7
- package/native/core/programmers/llm/LlmStructuredOutputProgrammer.go +2 -4
- package/native/core/programmers/llm/llm_schema_programmer_coverage_test.go +1 -1
- package/native/core/programmers/notations/NotationAssertGeneralProgrammer.go +1 -3
- package/native/core/programmers/notations/NotationGeneralProgrammer.go +26 -28
- package/native/core/programmers/notations/NotationIsGeneralProgrammer.go +1 -3
- package/native/core/programmers/notations/NotationValidateGeneralProgrammer.go +1 -3
- package/native/core/programmers/plain/PlainAssertClassifyProgrammer.go +2 -4
- package/native/core/programmers/plain/PlainAssertCloneProgrammer.go +2 -4
- package/native/core/programmers/plain/PlainAssertPruneProgrammer.go +2 -4
- package/native/core/programmers/plain/PlainClassifyProgrammer.go +44 -46
- package/native/core/programmers/plain/PlainCloneProgrammer.go +30 -32
- package/native/core/programmers/plain/PlainIsCloneProgrammer.go +2 -4
- package/native/core/programmers/plain/PlainIsPruneProgrammer.go +2 -4
- package/native/core/programmers/plain/PlainPruneProgrammer.go +26 -28
- package/native/core/programmers/plain/PlainValidateClassifyProgrammer.go +2 -4
- package/native/core/programmers/plain/PlainValidateCloneProgrammer.go +2 -4
- package/native/core/programmers/plain/PlainValidatePruneProgrammer.go +2 -4
- package/native/core/programmers/protobuf/ProtobufAssertDecodeProgrammer.go +2 -4
- package/native/core/programmers/protobuf/ProtobufAssertEncodeProgrammer.go +2 -4
- package/native/core/programmers/protobuf/ProtobufDecodeProgrammer.go +24 -25
- package/native/core/programmers/protobuf/ProtobufEncodeProgrammer.go +23 -25
- package/native/core/programmers/protobuf/ProtobufIsDecodeProgrammer.go +2 -4
- package/native/core/programmers/protobuf/ProtobufIsEncodeProgrammer.go +2 -4
- package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +1 -3
- package/native/core/programmers/protobuf/ProtobufValidateDecodeProgrammer.go +2 -4
- package/native/core/programmers/protobuf/ProtobufValidateEncodeProgrammer.go +3 -5
- package/native/core/programmers/protobuf/protobuf_programmer_helper_coverage_test.go +1 -1
- package/native/core/programmers/reflect/ReflectLiteralsProgrammer.go +1 -3
- package/native/core/schemas/metadata/MetadataCollection.go +2 -2
- package/native/core/schemas/metadata/MetadataDefaultLibrary.go +1 -1
- package/native/core/schemas/metadata/MetadataDependency.go +13 -13
- package/native/core/utils/PromiseTypeFactory.go +3 -3
- package/native/go.mod +3 -4
- package/native/go.work +1 -1
- package/native/transform/CallExpressionTransformer.go +4 -4
- package/native/transform/FileTransformer.go +2 -4
- package/native/transform/features/RandomTransformer.go +1 -3
- package/native/transform/features/compare/CompareEqualTransformer.go +1 -1
- package/native/transform/features/compare/CompareLessTransformer.go +1 -1
- package/native/transform/features/functional/FunctionalGenericTransformer.go +2 -2
- package/native/transform/features/llm/LlmCoerceTransformer.go +1 -1
- package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations.go +1 -1
- package/native/transform/features/llm/LlmEvaluationTransformer.go +1 -1
- package/native/transform/features/llm/LlmParseTransformer.go +1 -1
- package/native/transform/features/llm/LlmSchemaTransformer.go +1 -3
- package/native/transform/features/reflect/ReflectNameTransformer.go +1 -3
- package/native/transform/file_transformer_coverage_test.go +2 -1
- package/native/transform/internal/GenericTransformer.go +1 -3
- package/package.json +6 -6
- package/src/transform.ts +6 -2
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"crypto/sha256"
|
|
5
|
+
"fmt"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"regexp"
|
|
9
|
+
"strings"
|
|
10
|
+
"sync"
|
|
11
|
+
"testing"
|
|
12
|
+
|
|
13
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
14
|
+
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
15
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
16
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
17
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
18
|
+
"github.com/samchon/typia/packages/typia/native/adapter"
|
|
19
|
+
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
20
|
+
metadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
21
|
+
"github.com/samchon/typia/packages/typia/native/transform"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
// TestDistinctProgramsConcurrentEmit verifies independent Programs retain their
|
|
25
|
+
// own generated nodes during overlapping emits across programmer families.
|
|
26
|
+
//
|
|
27
|
+
// NodeFactory arenas are mutable. A helper dropping its emit context used to
|
|
28
|
+
// reach a package-global arena even when each worker owned its own Program.
|
|
29
|
+
// The serial control proves real validators were emitted before concurrency;
|
|
30
|
+
// the race-enabled canonical command additionally detects shared arena access.
|
|
31
|
+
//
|
|
32
|
+
// 1. Load two isolated Programs over the same type graph and separate markers.
|
|
33
|
+
// 2. Emit both serially and require generated assert/is helpers and own markers.
|
|
34
|
+
// 3. Emit sixteen times per Program concurrently and require identical bytes.
|
|
35
|
+
//
|
|
36
|
+
// @evidence contracts/testing.md#behavioral-verification Calls the real transform through EmitWithPluginTransformer with each file's EmitContext. Every output must contain both generated validator helpers and its authored marker; repeated parallel outputs must equal the serial output, and the race detector rejects shared mutable arenas.
|
|
37
|
+
// @evidence contracts/testing.md#independent-expectations The authored sources require string, number and array checks and distinct marker literals. The serial comparison only proves repeatability, not semantic correctness; the generated helper checks prevent untransformed stubs from passing, while existing TypeScript execution suites own runtime verdicts.
|
|
38
|
+
// @evidence contracts/testing.md#distinguishing-cases Serial controls precede overlapping workers, each owning one Program. Nested arrays, optional objects, unions and validator, JSON, HTTP, protobuf, notation, plain, random and functional producers exercise shared helper paths; foreign marker output and nondeterministic bytes fail independently of race detection.
|
|
39
|
+
// @evidence contracts/testing.md#execution-ownership Go's native unit population loads in-process Programs using shared immutable declarations and separate writable fixture roots. It builds no host binary and launches no JavaScript runtime; Program.Close and fixture cleanup release every test-owned resource.
|
|
40
|
+
func TestDistinctProgramsConcurrentEmit(t *testing.T) {
|
|
41
|
+
programs := make([]*driver.Program, 2)
|
|
42
|
+
for index := range programs {
|
|
43
|
+
root := ttscTypiaTestFixtureDirectory(t, "concurrent-emit-")
|
|
44
|
+
source := distinctProgramsConcurrentEmitSource + fmt.Sprintf("\nexport const owner = %q;\n", fmt.Sprintf("program-%d", index))
|
|
45
|
+
for name, text := range map[string]string{
|
|
46
|
+
"index.ts": source,
|
|
47
|
+
"tsconfig.json": `{"compilerOptions":{"target":"es2022","module":"nodenext","moduleResolution":"nodenext","strict":true,"skipLibCheck":true,"esModuleInterop":true,"outDir":"out"},"files":["index.ts"]}`,
|
|
48
|
+
} {
|
|
49
|
+
if err := os.WriteFile(filepath.Join(root, name), []byte(text), 0o644); err != nil {
|
|
50
|
+
t.Fatal(err)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
program, diagnostics, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{ForceEmit: true})
|
|
54
|
+
if err != nil || driver.CountErrors(diagnostics) != 0 {
|
|
55
|
+
t.Fatalf("load: %v; %+v", err, diagnostics)
|
|
56
|
+
}
|
|
57
|
+
t.Cleanup(func() {
|
|
58
|
+
if err := program.Close(); err != nil {
|
|
59
|
+
t.Error(err)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
if diagnostics := program.Diagnostics(); driver.CountErrors(diagnostics) != 0 {
|
|
63
|
+
t.Fatalf("typecheck: %+v", diagnostics)
|
|
64
|
+
}
|
|
65
|
+
programs[index] = program
|
|
66
|
+
}
|
|
67
|
+
emit := func(index int) (string, error) {
|
|
68
|
+
program := programs[index]
|
|
69
|
+
metadata.MetadataDefaultLibrary_register(program.Checker, func(source *shimast.SourceFile) bool { return source != nil && program.TSProgram.IsLibFile(source) })
|
|
70
|
+
defer metadata.MetadataDefaultLibrary_release(program.Checker)
|
|
71
|
+
options := (adapter.PluginOptions{}).TransformOptions()
|
|
72
|
+
var mu sync.Mutex
|
|
73
|
+
var transformed []*nativecontext.ITypiaDiagnostic
|
|
74
|
+
var javascript string
|
|
75
|
+
extras := nativecontext.ITypiaContext_Extras{Shared: &sync.Map{}, AddDiagnostic: func(d *nativecontext.ITypiaDiagnostic) int {
|
|
76
|
+
mu.Lock()
|
|
77
|
+
defer mu.Unlock()
|
|
78
|
+
transformed = append(transformed, d)
|
|
79
|
+
return len(transformed)
|
|
80
|
+
}}
|
|
81
|
+
diagnostics, err := program.EmitWithPluginTransformer(func(ec *shimprinter.EmitContext, source *shimast.SourceFile) *shimast.SourceFile {
|
|
82
|
+
return transform.Transform(program, &options, extras, ec)(source)
|
|
83
|
+
}, shimcompiler.WriteFile(func(name shimtspath.RootedFilePath, text string, _ *shimcompiler.WriteFileData) error {
|
|
84
|
+
mu.Lock()
|
|
85
|
+
defer mu.Unlock()
|
|
86
|
+
if filepath.Base(string(name)) == "index.js" {
|
|
87
|
+
javascript = text
|
|
88
|
+
}
|
|
89
|
+
return nil
|
|
90
|
+
}))
|
|
91
|
+
if err != nil || driver.CountErrors(diagnostics) != 0 || len(transformed) != 0 {
|
|
92
|
+
return "", fmt.Errorf("emit: %v; %+v; %+v", err, diagnostics, transformed)
|
|
93
|
+
}
|
|
94
|
+
for _, required := range []string{"const _ao0", "const _io0", "Array.isArray", fmt.Sprintf("program-%d", index)} {
|
|
95
|
+
if !strings.Contains(javascript, required) {
|
|
96
|
+
return "", fmt.Errorf("missing %q in generated output:\n%s", required, javascript)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if !regexp.MustCompile(`(?:"string"\s*===\s*typeof input\.name\b|typeof input\.name\s*===\s*"string")`).MatchString(javascript) {
|
|
100
|
+
return "", fmt.Errorf("missing name string guard:\n%s", javascript)
|
|
101
|
+
}
|
|
102
|
+
if strings.Contains(javascript, fmt.Sprintf("program-%d", 1-index)) {
|
|
103
|
+
return "", fmt.Errorf("output carries foreign Program marker")
|
|
104
|
+
}
|
|
105
|
+
return javascript, nil
|
|
106
|
+
}
|
|
107
|
+
controls := make([]string, len(programs))
|
|
108
|
+
for index := range programs {
|
|
109
|
+
var err error
|
|
110
|
+
controls[index], err = emit(index)
|
|
111
|
+
if err != nil {
|
|
112
|
+
t.Fatalf("serial: %v", err)
|
|
113
|
+
}
|
|
114
|
+
t.Logf("serial output %d SHA256: %x", index, sha256.Sum256([]byte(controls[index])))
|
|
115
|
+
}
|
|
116
|
+
start := make(chan struct{})
|
|
117
|
+
failures := make(chan error, len(programs))
|
|
118
|
+
var workers sync.WaitGroup
|
|
119
|
+
for index := range programs {
|
|
120
|
+
workers.Add(1)
|
|
121
|
+
go func() {
|
|
122
|
+
defer workers.Done()
|
|
123
|
+
<-start
|
|
124
|
+
for range 16 {
|
|
125
|
+
output, err := emit(index)
|
|
126
|
+
if err != nil {
|
|
127
|
+
failures <- err
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
if output != controls[index] {
|
|
131
|
+
failures <- fmt.Errorf("Program %d changed emitted bytes", index)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}()
|
|
136
|
+
}
|
|
137
|
+
close(start)
|
|
138
|
+
workers.Wait()
|
|
139
|
+
close(failures)
|
|
140
|
+
for err := range failures {
|
|
141
|
+
t.Error(err)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const distinctProgramsConcurrentEmitSource = `import typia from "typia";
|
|
146
|
+
interface RecordType {
|
|
147
|
+
name: string;
|
|
148
|
+
values: { key: string; value: number; enabled: boolean }[];
|
|
149
|
+
optional?: { nested: { label: string; count: number } };
|
|
150
|
+
union: { type: "a"; text: string } | { type: "b"; count: number };
|
|
151
|
+
}
|
|
152
|
+
export const assertRecord = typia.createAssert<RecordType>();
|
|
153
|
+
export const isRecord = typia.createIs<RecordType>();
|
|
154
|
+
export const validateRecord = typia.createValidate<RecordType>();
|
|
155
|
+
export const stringify = typia.json.createAssertStringify<RecordType>();
|
|
156
|
+
export const parse = typia.json.createAssertParse<RecordType>();
|
|
157
|
+
export const query = typia.http.createAssertQuery<{ name: string; values: number[] }>();
|
|
158
|
+
export const headers = typia.http.createAssertHeaders<{ name: string }>();
|
|
159
|
+
export const encode = typia.protobuf.createAssertEncode<RecordType>();
|
|
160
|
+
export const decode = typia.protobuf.createAssertDecode<RecordType>();
|
|
161
|
+
export const decodeMap = typia.protobuf.createDecode<{ values: Map<string, number> }>();
|
|
162
|
+
export const decodeRecord = typia.protobuf.createDecode<{ values: Record<string, number> }>();
|
|
163
|
+
export const camel = typia.notations.createCamel<RecordType>();
|
|
164
|
+
export const clone = typia.plain.createAssertClone<RecordType>();
|
|
165
|
+
export const random = typia.createRandom<RecordType>();
|
|
166
|
+
export const functional = typia.functional.assertFunction((input: RecordType): RecordType => input);
|
|
167
|
+
`
|
|
@@ -4,6 +4,7 @@ import (
|
|
|
4
4
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
5
5
|
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
6
6
|
shimparser "github.com/microsoft/typescript-go/shim/parser"
|
|
7
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
7
8
|
"os"
|
|
8
9
|
"path/filepath"
|
|
9
10
|
"strings"
|
|
@@ -52,7 +53,7 @@ export const randomIncluded=()=>typia.random<IExactOptionalClone>({boolean:()=>t
|
|
|
52
53
|
if code != 0 {
|
|
53
54
|
t.Fatalf("exact optional transform failed %d: %s", code, stderr)
|
|
54
55
|
}
|
|
55
|
-
file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: filepath.ToSlash(filepath.Join(project, "emitted.js"))}, output, shimcore.ScriptKindJS)
|
|
56
|
+
file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: shimtspath.RootedFilePath(filepath.ToSlash(filepath.Join(project, "emitted.js")))}, output, shimcore.ScriptKindJS)
|
|
56
57
|
bindings := map[string]*shimast.Node{}
|
|
57
58
|
assertImports := map[string]bool{}
|
|
58
59
|
var walk func(*shimast.Node) bool
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestProjectDependenciesHeritageFormsTransform verifies both type and value
|
|
11
|
+
// heritage retain empty bases and phantom type arguments in dependency reports.
|
|
12
|
+
//
|
|
13
|
+
// TypeScript 7.1 represents interface extends and class implements with type
|
|
14
|
+
// references, while class extends keeps an expression with type arguments.
|
|
15
|
+
// Empty bases and unused generic arguments cannot be recovered from properties.
|
|
16
|
+
//
|
|
17
|
+
// 1. Declare aliased and qualified empty generic interfaces, classes and contracts.
|
|
18
|
+
// 2. Transform validators for interface, class, implements and class-expression forms.
|
|
19
|
+
// 3. Require all written heritage and argument files, excluding an unused sibling.
|
|
20
|
+
//
|
|
21
|
+
// @evidence contracts/testing.md#behavioral-verification Calls the real project transform and checks main.ts dependencies include each empty interface, base class, implemented contract and phantom argument file. A valid plain class without heritage is transformed in the same project and an unconsumed sibling must remain absent.
|
|
22
|
+
// @evidence contracts/testing.md#independent-expectations Editing any authored empty base or phantom type argument can change the consumed type, so their written references belong to the dependency contract even before they contribute a property. The unrelated interface is never referenced and must not become a dependency.
|
|
23
|
+
// @evidence contracts/testing.md#distinguishing-cases Interface extends, class extends, class implements and class-expression extends/implements combine named aliases, qualified namespace names and generic arguments. The plain-class and unused-file controls distinguish absent heritage from over-reporting; the existing empty-heritage test owns the minimal interface witness.
|
|
24
|
+
// @evidence contracts/testing.md#execution-ownership The canonical native and 7.1 Go populations discover this in-process transform unit. It shares immutable declaration inputs through the existing fixture helper and starts no compiler or JavaScript subprocess.
|
|
25
|
+
func TestProjectDependenciesHeritageFormsTransform(t *testing.T) {
|
|
26
|
+
project := ttscTypiaTestFixtureDirectory(t, "heritage-forms-")
|
|
27
|
+
if err := os.MkdirAll(filepath.Join(project, "src"), 0o755); err != nil {
|
|
28
|
+
t.Fatal(err)
|
|
29
|
+
}
|
|
30
|
+
files := map[string]string{
|
|
31
|
+
"tsconfig.json": projectDependenciesEnvelopeTSConfig,
|
|
32
|
+
"src/interface-base.ts": `export namespace Types { export interface Empty<T> {} }`,
|
|
33
|
+
"src/class-base.ts": `export class Parent<T> {}`,
|
|
34
|
+
"src/contract.ts": `export interface Contract<T> {}`,
|
|
35
|
+
"src/tag.ts": `export interface Tag { id: string }`,
|
|
36
|
+
"src/unrelated.ts": `export interface Unused { unused: string }`,
|
|
37
|
+
"src/models.ts": `import { Types } from "./interface-base";
|
|
38
|
+
import { Parent as Base } from "./class-base";
|
|
39
|
+
import { Contract as ContractAlias } from "./contract";
|
|
40
|
+
import { Tag } from "./tag";
|
|
41
|
+
export interface ViaInterface extends Types.Empty<Tag> { id: string }
|
|
42
|
+
export class ViaClass extends Base<Tag> { id!: string }
|
|
43
|
+
export class ViaImplements implements ContractAlias<Tag> { id!: string }
|
|
44
|
+
export const Bound = class extends Base<Tag> implements ContractAlias<Tag> { id!: string };
|
|
45
|
+
export class Plain { id!: string }
|
|
46
|
+
`,
|
|
47
|
+
"src/main.ts": `import typia from "typia";
|
|
48
|
+
import { ViaInterface, ViaClass, ViaImplements, Bound, Plain } from "./models";
|
|
49
|
+
export const i = typia.createIs<ViaInterface>();
|
|
50
|
+
export const c = typia.createIs<ViaClass>();
|
|
51
|
+
export const m = (input: unknown) => typia.validate<ViaImplements>(input);
|
|
52
|
+
export const e = typia.createIs<InstanceType<typeof Bound>>();
|
|
53
|
+
export const p = typia.createIs<Plain>();
|
|
54
|
+
`,
|
|
55
|
+
}
|
|
56
|
+
for name, text := range files {
|
|
57
|
+
if err := os.WriteFile(filepath.Join(project, name), []byte(text), 0o644); err != nil {
|
|
58
|
+
t.Fatal(err)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
ttscTypiaTestTypecheck(t, project)
|
|
62
|
+
output, errors, code := ttscTypiaTestCapture(func() int { return runTransform([]string{"--cwd", project, "--output", "ts"}) })
|
|
63
|
+
if code != 0 {
|
|
64
|
+
t.Fatalf("heritage transform: %d\n%s", code, errors)
|
|
65
|
+
}
|
|
66
|
+
var envelope struct {
|
|
67
|
+
Dependencies map[string][]string `json:"dependencies"`
|
|
68
|
+
}
|
|
69
|
+
if err := json.Unmarshal([]byte(output), &envelope); err != nil {
|
|
70
|
+
t.Fatal(err)
|
|
71
|
+
}
|
|
72
|
+
found := map[string]bool{}
|
|
73
|
+
for _, name := range envelope.Dependencies["src/main.ts"] {
|
|
74
|
+
found[name] = true
|
|
75
|
+
}
|
|
76
|
+
for _, name := range []string{"models", "interface-base", "class-base", "contract", "tag"} {
|
|
77
|
+
if !found["src/"+name+".ts"] {
|
|
78
|
+
t.Errorf("missing %s heritage dependency: %v", name, found)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if found["src/unrelated.ts"] {
|
|
82
|
+
t.Errorf("unconsumed heritage was reported: %v", found)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -9,13 +9,14 @@ import (
|
|
|
9
9
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
10
|
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
11
11
|
shimparser "github.com/microsoft/typescript-go/shim/parser"
|
|
12
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
// ttscTypiaTestSchemaLiteral decodes the emitted schema object's AST without
|
|
15
16
|
// executing JavaScript. It rejects expressions rather than evaluating them.
|
|
16
17
|
func ttscTypiaTestSchemaLiteral(t *testing.T, output string) map[string]any {
|
|
17
18
|
t.Helper()
|
|
18
|
-
file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: filepath.ToSlash(filepath.Join(t.TempDir(), "emitted.ts"))}, output, shimcore.ScriptKindTS)
|
|
19
|
+
file := shimparser.ParseSourceFile(shimast.SourceFileParseOptions{FileName: shimtspath.RootedFilePath(filepath.ToSlash(filepath.Join(t.TempDir(), "emitted.ts")))}, output, shimcore.ScriptKindTS)
|
|
19
20
|
var found map[string]any
|
|
20
21
|
var walk func(*shimast.Node) bool
|
|
21
22
|
walk = func(node *shimast.Node) bool {
|
|
@@ -14,6 +14,7 @@ import (
|
|
|
14
14
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
15
15
|
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
16
16
|
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
17
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
17
18
|
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
18
19
|
typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
|
|
19
20
|
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
@@ -250,7 +251,7 @@ func runTransformProject(
|
|
|
250
251
|
if sf.IsDeclarationFile {
|
|
251
252
|
continue
|
|
252
253
|
}
|
|
253
|
-
key := driver.TransformOutputKey(cwd, sf.FileName())
|
|
254
|
+
key := driver.TransformOutputKey(cwd, string(sf.FileName()))
|
|
254
255
|
if filepath.IsAbs(key) || key == ".." || strings.HasPrefix(key, "../") {
|
|
255
256
|
continue
|
|
256
257
|
}
|
|
@@ -332,13 +333,13 @@ func transformSingleToJavaScript(
|
|
|
332
333
|
// itself. Nothing the emit writes is then this file's artifact, so guard the
|
|
333
334
|
// comparison rather than let an empty expectation match anything.
|
|
334
335
|
expected := ""
|
|
335
|
-
if paths := shimcompiler.
|
|
336
|
-
expected = filepath.ToSlash(paths.JsFilePath())
|
|
336
|
+
if paths := shimcompiler.OutputPathsFor(target, prog.TSProgram.Options(), prog.TSProgram); paths != nil {
|
|
337
|
+
expected = filepath.ToSlash(string(paths.JsFilePath()))
|
|
337
338
|
}
|
|
338
339
|
var captured string
|
|
339
340
|
found := false
|
|
340
|
-
writeFile := shimcompiler.WriteFile(func(fileName, text string, _ *shimcompiler.WriteFileData) error {
|
|
341
|
-
if expected != "" && filepath.ToSlash(fileName) == expected {
|
|
341
|
+
writeFile := shimcompiler.WriteFile(func(fileName shimtspath.RootedFilePath, text string, _ *shimcompiler.WriteFileData) error {
|
|
342
|
+
if expected != "" && filepath.ToSlash(string(fileName)) == expected {
|
|
342
343
|
captured = text
|
|
343
344
|
found = true
|
|
344
345
|
}
|
|
@@ -349,7 +350,7 @@ func transformSingleToJavaScript(
|
|
|
349
350
|
return "", 3
|
|
350
351
|
}
|
|
351
352
|
if !found {
|
|
352
|
-
fmt.Fprintf(stderr, "ttsc-typia transform: no output produced for %s\n", target.FileName())
|
|
353
|
+
fmt.Fprintf(stderr, "ttsc-typia transform: no output produced for %s\n", string(target.FileName()))
|
|
353
354
|
return "", 3
|
|
354
355
|
}
|
|
355
356
|
return captured, 0
|
|
@@ -5,24 +5,18 @@ import (
|
|
|
5
5
|
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
6
6
|
)
|
|
7
7
|
|
|
8
|
-
// EmitFactory
|
|
8
|
+
// EmitFactory selects a live emit context's factory or the caller's explicit
|
|
9
|
+
// standalone fallback. Returning the same instance preserves its compiler or
|
|
10
|
+
// caller-installed hooks. A nil fallback remains nil when no context is supplied.
|
|
9
11
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// namespace alias) that the tsgo emit pipeline relies on. When ec is nil (legacy
|
|
14
|
-
// text emit, analysis-only or test paths) the caller's standalone fallback
|
|
15
|
-
// factory is returned unchanged.
|
|
12
|
+
// The caller owns the fallback and must confine its mutable arena to its own
|
|
13
|
+
// construction lifetime. This selector retains no factory; callers that need a
|
|
14
|
+
// fresh standalone arena can use NodeFactoryOf instead.
|
|
16
15
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// @evidence contracts/common.md#principled-implementation A live emit context returns its embedded node factory, whose hooks give created nodes original-node links and emit metadata, and otherwise the caller's standalone factory is returned unchanged; both print the same tokens.
|
|
23
|
-
// @evidence contracts/common.md#clear-and-simple-design One conditional that replaces the same branch repeated in every node-creating helper.
|
|
24
|
-
// @evidence contracts/common.md#prohibited-implementation-shortcuts The fallback is the supported analysis and test path and not a workaround for a missing context.
|
|
25
|
-
// @evidence contracts/common.md#meaningful-documentation The doc states both outcomes and why the emit factory is preferred.
|
|
16
|
+
// @evidence contracts/common.md#principled-implementation A live context returns its embedded factory to retain compiler emit hooks. Otherwise the exact supplied fallback, including nil, is returned so caller ownership and custom hooks remain intact; the selector allocates and retains no arena.
|
|
17
|
+
// @evidence contracts/common.md#clear-and-simple-design One conditional implements the explicit-fallback selection contract used by native consumers, separately from NodeFactoryOf's fresh-allocation contract.
|
|
18
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts Selection uses the public compiler factory boundary without foreign mutation, shared state or serialization. The fallback is caller-owned; typia's own context-free construction uses fresh factories through NodeFactoryOf.
|
|
19
|
+
// @evidence contracts/common.md#meaningful-documentation The comment states selection priority, nil behavior, hook preservation and the caller's arena ownership responsibility.
|
|
26
20
|
func EmitFactory(ec *shimprinter.EmitContext, fallback *shimast.NodeFactory) *shimast.NodeFactory {
|
|
27
21
|
if ec != nil {
|
|
28
22
|
return &ec.Factory.NodeFactory
|
|
@@ -30,20 +24,17 @@ func EmitFactory(ec *shimprinter.EmitContext, fallback *shimast.NodeFactory) *sh
|
|
|
30
24
|
return fallback
|
|
31
25
|
}
|
|
32
26
|
|
|
33
|
-
// EmitFactoryOf
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// standalone fallback, which is the legacy / analysis-only / test path; passing
|
|
37
|
-
// a live context routes creation through ec.Factory. This lets call sites thread
|
|
38
|
-
// the real context in progressively while the build stays green at every step.
|
|
27
|
+
// EmitFactoryOf selects the first optional context through EmitFactory.
|
|
28
|
+
// An omitted or nil first context selects the caller's fallback even when a
|
|
29
|
+
// later context is live; no factory is allocated or retained by the selector.
|
|
39
30
|
//
|
|
40
|
-
// @evidence contracts/common.md#principled-implementation
|
|
41
|
-
// @evidence contracts/common.md#clear-and-simple-design
|
|
42
|
-
// @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
43
|
-
// @evidence contracts/common.md#meaningful-documentation The
|
|
31
|
+
// @evidence contracts/common.md#principled-implementation Only the first optional context participates, preserving the existing variadic consumer contract. EmitFactory owns the same live-context versus caller-fallback decision for both entry forms.
|
|
32
|
+
// @evidence contracts/common.md#clear-and-simple-design A slice-presence check adapts optional arguments to the scalar selector without duplicating its factory selection.
|
|
33
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The adapter changes neither the caller's factory nor its context and stores no shared mutable arena.
|
|
34
|
+
// @evidence contracts/common.md#meaningful-documentation The comment explains omitted, nil-first and later-context behavior and makes the absence of allocation and retained state explicit.
|
|
44
35
|
func EmitFactoryOf(fallback *shimast.NodeFactory, emit ...*shimprinter.EmitContext) *shimast.NodeFactory {
|
|
45
|
-
if len(emit)
|
|
46
|
-
return
|
|
36
|
+
if len(emit) == 0 {
|
|
37
|
+
return EmitFactory(nil, fallback)
|
|
47
38
|
}
|
|
48
|
-
return fallback
|
|
39
|
+
return EmitFactory(emit[0], fallback)
|
|
49
40
|
}
|
|
@@ -130,8 +130,6 @@ type importProgrammer_asset struct {
|
|
|
130
130
|
order []string
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
var importProgrammer_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
|
|
134
|
-
|
|
135
133
|
// NewImportProgrammer creates an importer with the first option record, or the
|
|
136
134
|
// zero options.
|
|
137
135
|
//
|
|
@@ -213,7 +211,7 @@ func (p *ImportProgrammer) Default(props ImportProgrammer_IDefault) *shimast.Nod
|
|
|
213
211
|
if p.emit_ != nil {
|
|
214
212
|
return p.member(asset, "default")
|
|
215
213
|
}
|
|
216
|
-
return
|
|
214
|
+
return NodeFactoryOf(p.emit_).NewIdentifier(asset.Default.Name)
|
|
217
215
|
}
|
|
218
216
|
|
|
219
217
|
// Instance records a named import and returns its reference. The alias, or the
|
|
@@ -236,7 +234,7 @@ func (p *ImportProgrammer) Instance(props ImportProgrammer_IInstance) *shimast.N
|
|
|
236
234
|
if p.emit_ != nil {
|
|
237
235
|
return p.member(asset, props.Name)
|
|
238
236
|
}
|
|
239
|
-
return
|
|
237
|
+
return NodeFactoryOf(p.emit_).NewIdentifier(alias)
|
|
240
238
|
}
|
|
241
239
|
|
|
242
240
|
// Namespace records a namespace import of the file and returns its reference. The
|
|
@@ -255,7 +253,7 @@ func (p *ImportProgrammer) Namespace(props ImportProgrammer_INamespace) *shimast
|
|
|
255
253
|
if p.emit_ != nil {
|
|
256
254
|
return p.namespaceName(asset)
|
|
257
255
|
}
|
|
258
|
-
return
|
|
256
|
+
return NodeFactoryOf(p.emit_).NewIdentifier(asset.namespace.Name)
|
|
259
257
|
}
|
|
260
258
|
|
|
261
259
|
// Type builds an import type node `import("file").Qualifier<Arguments>`. It
|
|
@@ -266,7 +264,7 @@ func (p *ImportProgrammer) Namespace(props ImportProgrammer_INamespace) *shimast
|
|
|
266
264
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts An unsupported qualifier type produces a node without a qualifier and does not panic.
|
|
267
265
|
// @evidence contracts/common.md#meaningful-documentation The doc states that nothing is registered.
|
|
268
266
|
func (p *ImportProgrammer) Type(props ImportProgrammer_TypeProps) *shimast.Node {
|
|
269
|
-
f :=
|
|
267
|
+
f := NodeFactoryOf(p.emit_)
|
|
270
268
|
var qualifier *shimast.EntityName
|
|
271
269
|
switch name := props.Name.(type) {
|
|
272
270
|
case string:
|
|
@@ -343,7 +341,7 @@ func (p *ImportProgrammer) GetInternalText(name string) string {
|
|
|
343
341
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts The ranking is a fixed table and the order is deterministic.
|
|
344
342
|
// @evidence contracts/common.md#meaningful-documentation The doc states the order and the two forms.
|
|
345
343
|
func (p *ImportProgrammer) ToStatements() []*shimast.Node {
|
|
346
|
-
f :=
|
|
344
|
+
f := NodeFactoryOf(p.emit_)
|
|
347
345
|
statements := []*shimast.Node{}
|
|
348
346
|
order := append([]string{}, p.order_...)
|
|
349
347
|
indices := map[string]int{}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package context
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
5
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// NodeFactoryOf returns the first emit context's factory, or a fresh standalone
|
|
9
|
+
// factory when that context is absent or nil. Standalone callers own the factory
|
|
10
|
+
// and its nodes; no mutable arena is shared between independent calls.
|
|
11
|
+
//
|
|
12
|
+
// An emit factory retains the compiler's original-node and parent metadata.
|
|
13
|
+
// Helpers should forward their context throughout a transform. A standalone
|
|
14
|
+
// factory has no emit hooks and serves analysis and context-free construction.
|
|
15
|
+
//
|
|
16
|
+
// @evidence contracts/common.md#principled-implementation A live first context returns its embedded node factory, whose hooks maintain emit provenance. Otherwise the supported ast constructor supplies a new arena owned by this caller, so independent Programs cannot race through a fallback factory.
|
|
17
|
+
// @evidence contracts/common.md#clear-and-simple-design One resolver owns both optional-context selection and standalone allocation; node-building helpers retain no package-global factory.
|
|
18
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The resolver uses the compiler's public factory boundary without modifying foreign state or serializing independent Programs. Missing contexts receive independent factories rather than a shared compensation.
|
|
19
|
+
// @evidence contracts/common.md#meaningful-documentation The comment distinguishes per-emit provenance from standalone construction and states ownership and nil behavior.
|
|
20
|
+
func NodeFactoryOf(emit ...*shimprinter.EmitContext) *shimast.NodeFactory {
|
|
21
|
+
if len(emit) != 0 && emit[0] != nil {
|
|
22
|
+
return &emit[0].Factory.NodeFactory
|
|
23
|
+
}
|
|
24
|
+
return shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
|
|
25
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package context
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
|
|
6
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestNodeFactoryOwnership verifies standalone arenas are independent while a
|
|
10
|
+
// live first emit context preserves its compiler factory identity.
|
|
11
|
+
//
|
|
12
|
+
// A shared no-context fallback races even when all live contexts are distinct.
|
|
13
|
+
// Allocating separately also prevents a later standalone caller from changing
|
|
14
|
+
// nodes retained by an earlier caller.
|
|
15
|
+
//
|
|
16
|
+
// 1. Select factories with omitted and explicit nil contexts and compare identity.
|
|
17
|
+
// 2. Select a live context twice and require its embedded factory both times.
|
|
18
|
+
// 3. Allocate distinct identifiers and require the earlier identifier to survive.
|
|
19
|
+
//
|
|
20
|
+
// @evidence contracts/testing.md#behavioral-verification Calls NodeFactoryOf with absent, nil and live contexts, asserts independent fallback pointer identities and exact live factory identity, then checks identifier text survives allocations in the other factories.
|
|
21
|
+
// @evidence contracts/testing.md#independent-expectations The ownership contract requires no arena sharing across independent standalone requests and requires live compiler factory identity to retain emit hooks. Authored identifier strings supply the content oracle.
|
|
22
|
+
// @evidence contracts/testing.md#distinguishing-cases Omitted, explicit nil, repeated live and nil-first-with-live-second contexts distinguish first-argument selection and fallback independence; a different context must select a different live factory.
|
|
23
|
+
// @evidence contracts/testing.md#execution-ownership Go's native unit population discovers this same-package resolver test through test:go:native and test:go:7.1. It uses real in-memory compiler factories and starts no process or installed consumer.
|
|
24
|
+
func TestNodeFactoryOwnership(t *testing.T) {
|
|
25
|
+
first, second := NodeFactoryOf(), NodeFactoryOf(nil)
|
|
26
|
+
live := shimprinter.NewEmitContext()
|
|
27
|
+
third := NodeFactoryOf(nil, live)
|
|
28
|
+
if first == second || first == third || second == third {
|
|
29
|
+
t.Fatal("standalone callers share a mutable arena")
|
|
30
|
+
}
|
|
31
|
+
if NodeFactoryOf(live) != &live.Factory.NodeFactory || NodeFactoryOf(live) != NodeFactoryOf(live) {
|
|
32
|
+
t.Fatal("live emit factory identity was lost")
|
|
33
|
+
}
|
|
34
|
+
if NodeFactoryOf(live) == NodeFactoryOf(shimprinter.NewEmitContext()) {
|
|
35
|
+
t.Fatal("independent emit contexts share a factory")
|
|
36
|
+
}
|
|
37
|
+
original := first.NewIdentifier("first")
|
|
38
|
+
for range 1024 {
|
|
39
|
+
second.NewIdentifier("second")
|
|
40
|
+
third.NewIdentifier("third")
|
|
41
|
+
}
|
|
42
|
+
if original.Text() != "first" {
|
|
43
|
+
t.Fatalf("standalone allocation corrupted retained text: %s", original.Text())
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -3,6 +3,7 @@ package factories
|
|
|
3
3
|
import (
|
|
4
4
|
"crypto/rand"
|
|
5
5
|
"fmt"
|
|
6
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
6
7
|
"math"
|
|
7
8
|
"os"
|
|
8
9
|
"path/filepath"
|
|
@@ -153,10 +154,8 @@ type ExpressionFactory_GetEscapedTextProps struct {
|
|
|
153
154
|
Input *shimast.Expression
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
var expressionFactory_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
|
|
157
|
-
|
|
158
157
|
func (expressionFactoryNamespace) Number(value any, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
159
|
-
f := nativecontext.
|
|
158
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
160
159
|
text, numeric := expressionFactory_number_text(value)
|
|
161
160
|
// Go spells the non-finite floats `NaN`, `+Inf`, and `-Inf`. Only the first
|
|
162
161
|
// happens to be JavaScript; `+Inf` evaluates an undeclared `Inf` and throws a
|
|
@@ -193,7 +192,7 @@ func (expressionFactoryNamespace) Number(value any, emit ...*shimprinter.EmitCon
|
|
|
193
192
|
// `BigInt` parses the digits of a string directly, so the string form is exact
|
|
194
193
|
// at any magnitude.
|
|
195
194
|
func (expressionFactoryNamespace) Bigint(value any, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
196
|
-
f := nativecontext.
|
|
195
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
197
196
|
return f.NewCallExpression(
|
|
198
197
|
f.NewIdentifier("BigInt"),
|
|
199
198
|
nil,
|
|
@@ -206,7 +205,7 @@ func (expressionFactoryNamespace) Bigint(value any, emit ...*shimprinter.EmitCon
|
|
|
206
205
|
}
|
|
207
206
|
|
|
208
207
|
func (expressionFactoryNamespace) IsRequired(input *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
209
|
-
f := nativecontext.
|
|
208
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
210
209
|
return f.NewBinaryExpression(
|
|
211
210
|
nil,
|
|
212
211
|
f.NewIdentifier("undefined"),
|
|
@@ -217,7 +216,7 @@ func (expressionFactoryNamespace) IsRequired(input *shimast.Expression, emit ...
|
|
|
217
216
|
}
|
|
218
217
|
|
|
219
218
|
func (expressionFactoryNamespace) IsArray(input *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
220
|
-
f := nativecontext.
|
|
219
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
221
220
|
return f.NewCallExpression(
|
|
222
221
|
f.NewIdentifier("Array.isArray"),
|
|
223
222
|
nil,
|
|
@@ -228,7 +227,7 @@ func (expressionFactoryNamespace) IsArray(input *shimast.Expression, emit ...*sh
|
|
|
228
227
|
}
|
|
229
228
|
|
|
230
229
|
func (expressionFactoryNamespace) IsObject(props ExpressionFactory_IsObjectProps, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
231
|
-
f := nativecontext.
|
|
230
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
232
231
|
conditions := []*shimast.Node{
|
|
233
232
|
f.NewBinaryExpression(
|
|
234
233
|
nil,
|
|
@@ -273,7 +272,7 @@ func (expressionFactoryNamespace) IsObject(props ExpressionFactory_IsObjectProps
|
|
|
273
272
|
}
|
|
274
273
|
|
|
275
274
|
func (expressionFactoryNamespace) IsInstanceOf(t string, input *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
276
|
-
f := nativecontext.
|
|
275
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
277
276
|
return f.NewBinaryExpression(
|
|
278
277
|
nil,
|
|
279
278
|
input,
|
|
@@ -284,7 +283,7 @@ func (expressionFactoryNamespace) IsInstanceOf(t string, input *shimast.Expressi
|
|
|
284
283
|
}
|
|
285
284
|
|
|
286
285
|
func (expressionFactoryNamespace) Coalesce(x *shimast.Expression, y *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
287
|
-
f := nativecontext.
|
|
286
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
288
287
|
return f.NewBinaryExpression(
|
|
289
288
|
nil,
|
|
290
289
|
x,
|
|
@@ -301,7 +300,7 @@ func (expressionFactoryNamespace) Coalesce(x *shimast.Expression, y *shimast.Exp
|
|
|
301
300
|
// removes that workaround. The node is structurally identical to the patched-up
|
|
302
301
|
// form, so emitted text is unchanged.
|
|
303
302
|
func (expressionFactoryNamespace) Conditional(condition *shimast.Expression, whenTrue *shimast.Expression, whenFalse *shimast.Expression, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
304
|
-
f := nativecontext.
|
|
303
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
305
304
|
return f.NewConditionalExpression(
|
|
306
305
|
condition,
|
|
307
306
|
f.NewToken(shimast.KindQuestionToken),
|
|
@@ -312,7 +311,7 @@ func (expressionFactoryNamespace) Conditional(condition *shimast.Expression, whe
|
|
|
312
311
|
}
|
|
313
312
|
|
|
314
313
|
func (expressionFactoryNamespace) Currying(props ExpressionFactory_CurryingProps, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
315
|
-
f := nativecontext.
|
|
314
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
316
315
|
if len(props.Arguments) == 0 {
|
|
317
316
|
return f.NewCallExpression(props.Function, nil, nil, nil, shimast.NodeFlagsNone)
|
|
318
317
|
}
|
|
@@ -342,7 +341,7 @@ func (expressionFactoryNamespace) SelfCall(ec *shimprinter.EmitContext, body *sh
|
|
|
342
341
|
if body != nil && body.Kind == shimast.KindCallExpression {
|
|
343
342
|
return body
|
|
344
343
|
}
|
|
345
|
-
f := nativecontext.
|
|
344
|
+
f := nativecontext.NodeFactoryOf(ec)
|
|
346
345
|
var output *shimast.TypeNode
|
|
347
346
|
if len(typeNode) != 0 {
|
|
348
347
|
output = typeNode[0]
|
|
@@ -376,13 +375,13 @@ func (expressionFactoryNamespace) GetEscapedText(props ExpressionFactory_GetEsca
|
|
|
376
375
|
}
|
|
377
376
|
|
|
378
377
|
func (expressionFactoryNamespace) Transpile(props ExpressionFactory_TranspileProps, emit ...*shimprinter.EmitContext) func(input *shimast.Expression) *shimast.Node {
|
|
379
|
-
f := nativecontext.
|
|
378
|
+
f := nativecontext.NodeFactoryOf(emit...)
|
|
380
379
|
fileName := filepath.Join(os.TempDir(), expressionFactory_random_format_uuid()+".ts")
|
|
381
380
|
if absolute, err := filepath.Abs(fileName); err == nil {
|
|
382
381
|
fileName = absolute
|
|
383
382
|
}
|
|
384
383
|
file := shimparser.ParseSourceFile(
|
|
385
|
-
shimast.SourceFileParseOptions{FileName: filepath.ToSlash(fileName)},
|
|
384
|
+
shimast.SourceFileParseOptions{FileName: shimtspath.RootedFilePath(filepath.ToSlash(fileName))},
|
|
386
385
|
props.Script,
|
|
387
386
|
shimcore.ScriptKindTS,
|
|
388
387
|
)
|