-
Notifications
You must be signed in to change notification settings - Fork 202
Commit
…5247) #### General Vibe - This is an MVP of getting TS working via the wing cli (`compile`, `run`, and `test`). This is basically only usable for preflight - I wanted to change as little as possible in the SDK to avoid wasted work once resources are moved to winglibs - Does not address inflight stuff yet The entrypoint TS experience is different than the existing issue/story: ```ts import { main } from "ts4wing"; main((app) => { // stuff goes here }) ``` This was due to our usage of a "root" construct for testing isolation. The wing cli needs to have control over the instantiation of resources, so `new App()` is not really feasible because we don't want users to actually attach stuff at the app-level. It may be possible to get it working with `new App()` but the hacks needed didn't seem worth it. An added benefit of this approach is that the user no longer needs to call `app.synth()` Anecdotally, I have built an awscdk-based internal framework at a previous job that looked like this for a similar reason (to duplicate stuff across Stages) and it worked pretty well. #### Indirect Changes - Made sure everything matches their `@types/node` version - **preflight execution now happens in a worker thread instead of a VM** - VM context did not allow data to be provided past the initially loaded file. - In a VM, process.env is useful but pollutes the parent process - The overloaded "require" was only useful for the initial file. Now I added a shim that overloads require everywhere to make sure the SDK is loaded properly Fixes #3678 *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# TypeScript Wing Project |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "ts-fixture", | ||
"version": "0.0.0", | ||
"scripts": { | ||
"compile": "wing compile ./src/main.ts", | ||
"test": "wing test ./src/main.ts" | ||
}, | ||
"dependencies": { | ||
"ts4w": "workspace:^", | ||
"@winglang/sdk": "workspace:^", | ||
"winglang": "workspace:^" | ||
}, | ||
"volta": { | ||
"extends": "../../package.json" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { main } from "ts4w"; | ||
import { cloud } from "@winglang/sdk"; | ||
|
||
main((app) => { | ||
new cloud.Bucket(app, "Bucket"); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"$schema": "https://turborepo.org/schema.json", | ||
"extends": ["//"], | ||
"pipeline": { | ||
"compile": { | ||
"outputs": ["src/target/**"] | ||
}, | ||
"test": {} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
dist/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# ts4w - Experimental TypeScript experience for Wing | ||
|
||
```ts | ||
// main.ts | ||
import { main } from "ts4w"; | ||
import { cloud } from "@winglang/sdk"; | ||
|
||
main((app) => { | ||
new cloud.Bucket(app, "Bucket"); | ||
}) | ||
``` | ||
|
||
```shell | ||
wing compile main.ts | ||
wing compile -t tf-aws main.ts | ||
wing test main.ts | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
{ | ||
"name": "ts4w", | ||
"version": "0.0.0", | ||
"author": { | ||
"name": "Wing Cloud", | ||
"email": "[email protected]", | ||
"organization": true | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/winglang/wing.git", | ||
"directory": "libs/ts4w" | ||
}, | ||
"description": "Experimental TypeScript experience for Wing", | ||
"main": "dist/index.js", | ||
"license": "MIT", | ||
"scripts": { | ||
"compile": "tsup", | ||
"package": "bump-pack -b" | ||
}, | ||
"publishConfig": { | ||
"access": "public", | ||
"registry": "https://registry.npmjs.org", | ||
"tag": "latest" | ||
}, | ||
"dependencies": { | ||
"@winglang/sdk": "workspace:^", | ||
"constructs": "~10.2.69" | ||
}, | ||
"peerDependencies": { | ||
"typescript": "^5.3.3" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^18.17.13", | ||
"tsup": "^8.0.1", | ||
"bump-pack": "workspace:^" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"volta": { | ||
"extends": "../../package.json" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { std, platform } from "@winglang/sdk"; | ||
import { Construct } from "constructs"; | ||
|
||
export * as internal from "./internal"; | ||
|
||
/** | ||
* Properties for a Wing app. | ||
*/ | ||
export interface AppProps { | ||
/** | ||
* The name and id of the app. | ||
* @default "main" | ||
*/ | ||
name?: string; | ||
} | ||
|
||
/** | ||
* Create a Wing app. | ||
* | ||
* ```ts | ||
* import { main } from "ts4w"; | ||
* import { cloud } from "@winglang/sdk"; | ||
* | ||
* main(app => { | ||
* new cloud.Bucket(app, "Bucket"); | ||
* }); | ||
* ``` | ||
* | ||
* @param fn Define your application using the provided root construct. | ||
* Note that this function may be called multiple times when used with `wing test`. | ||
*/ | ||
export function main(fn: (root: Construct) => void, props: AppProps = {}) { | ||
// check if we have everything we need | ||
const requiredEnvVars = [ | ||
"WING_PLATFORMS", | ||
"WING_SYNTH_DIR", | ||
"WING_SOURCE_DIR", | ||
"WING_IS_TEST", | ||
]; | ||
for (const envVar of requiredEnvVars) { | ||
if (process.env[envVar] === undefined) { | ||
throw new Error(`\ | ||
Missing environment variable: ${envVar} | ||
This is a Wing app and must be run through the Wing CLI (npm install -f winglang).`); | ||
} | ||
} | ||
|
||
class $Root extends std.Resource { | ||
constructor(scope: Construct, id: string) { | ||
super(scope, id); | ||
fn(this); | ||
} | ||
} | ||
|
||
const platformPaths = ((s) => (!s ? [] : s.split(";")))( | ||
process.env.WING_PLATFORMS | ||
); | ||
const outdir = process.env.WING_SYNTH_DIR; | ||
const name = props.name ?? "main"; | ||
const rootConstruct = $Root; | ||
const isTestEnvironment = process.env.WING_IS_TEST === "true"; | ||
const entrypointDir = process.env.WING_SOURCE_DIR!; | ||
const rootId = process.env.WING_ROOT_ID; | ||
|
||
const $PlatformManager = new platform.PlatformManager({ platformPaths }); | ||
const app = $PlatformManager.createApp({ | ||
outdir, | ||
name, | ||
rootConstruct, | ||
isTestEnvironment, | ||
entrypointDir, | ||
rootId, | ||
}); | ||
|
||
app.synth(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { join } from "path"; | ||
|
||
export interface CompileOptions { | ||
workDir: string; | ||
entrypoint: string; | ||
} | ||
|
||
export async function compile(options: CompileOptions) { | ||
const ts = (await import("typescript")).default; | ||
const outDir = join(options.workDir, "ts"); | ||
|
||
const program = ts.createProgram([options.entrypoint], { | ||
target: ts.ScriptTarget.ES2022, | ||
module: ts.ModuleKind.CommonJS, | ||
moduleResolution: ts.ModuleResolutionKind.NodeJs, | ||
alwaysStrict: true, | ||
allowSyntheticDefaultImports: true, | ||
esModuleInterop: true, | ||
strict: true, | ||
sourceMap: true, | ||
outDir, | ||
noEmitOnError: true, | ||
listEmittedFiles: true, | ||
}) | ||
const results = program.emit(); | ||
|
||
for (const diagnostic of results.diagnostics) { | ||
console.error(diagnostic.messageText); | ||
} | ||
if (results.emitSkipped) { | ||
throw new Error("TS compilation failed"); | ||
} | ||
|
||
// get the last .js file emitted, this should be the entrypoint | ||
const emittedFiles = results.emittedFiles?.filter((f) => f.endsWith(".js")); | ||
const emittedFile = emittedFiles?.[emittedFiles.length - 1]; | ||
|
||
if (!emittedFile) { | ||
throw new Error(`TS compilation failed: Could not find emitted file in ${outDir}`); | ||
} | ||
|
||
return emittedFile; | ||
} |