Skip to content

Commit

Permalink
refactor: import esm modules in ide extension
Browse files Browse the repository at this point in the history
  • Loading branch information
felixhaeberle committed May 15, 2023
1 parent 31862cb commit 6994510
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 19 deletions.
129 changes: 127 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion source-code/ide-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,15 @@
"throttle-debounce": "^5.0.0"
},
"devDependencies": {
"@types/fs-extra": "^11.0.1",
"@types/throttle-debounce": "^5.0.0",
"@types/vscode": "^1.75.0",
"@vscode/vsce": "^2.18.0",
"esbuild": "^0.17.18",
"fs-extra": "^11.1.1",
"node-fetch": "^3.3.1",
"ovsx": "^0.8.0"
"ovsx": "^0.8.0",
"typescript": "^5.0.4"
},
"activationEvents": [
"workspaceContains:**/inlang.config.js"
Expand Down
16 changes: 8 additions & 8 deletions source-code/ide-extension/src/commands/extractMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { setState, state } from "../state.js"
import { query } from "@inlang/core/query"
import type { Message } from "@inlang/core/ast"
import { msg } from "../utils/message.js"
import { telemetryNode } from "@inlang/telemetry"
// import { telemetryNode } from "@inlang/telemetry"

/**
* Helps the user to extract messages from the active text editor.
Expand Down Expand Up @@ -106,13 +106,13 @@ export const extractMessageCommand = {
await textEditor.edit((editor) => {
editor.replace(textEditor.selection, preparedExtractOption)
})
telemetryNode.capture({
distinctId: "unknown",
event: "IDE-EXTENSION message extracted",
properties: {
config: state().config,
},
})
// telemetryNode.capture({

This comment has been minimized.

Copy link
@samuelstroschein

samuelstroschein May 15, 2023

Member

@felixhaeberle telemetry had nothing to do with the bug did it? In that case, re-enable telemetry?

// distinctId: "unknown",
// event: "IDE-EXTENSION message extracted",
// properties: {
// config: state().config,
// },
// })
return msg("Message extracted.")
},
} as const
17 changes: 9 additions & 8 deletions source-code/ide-extension/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { ExtractMessage } from "./actions/extractMessage.js"
import { createFileSystemMapper } from "./utils/createFileSystemMapper.js"
import { initialize$import } from "@inlang/core/environment"
import { msg } from "./utils/message.js"
import { telemetryNode } from "@inlang/telemetry"
import { $import } from "./utils/$import.js"
// import { telemetryNode } from "@inlang/telemetry"

export async function activate(context: vscode.ExtensionContext): Promise<void> {
try {
telemetryNode.capture({
distinctId: "unknown",
event: "IDE-EXTENSION activated",
})
// telemetryNode.capture({
// distinctId: "unknown",
// event: "IDE-EXTENSION activated",
// })
msg("Inlang extension activated.", "info")

// start the extension
Expand Down Expand Up @@ -77,7 +78,9 @@ async function main(args: { context: vscode.ExtensionContext }): Promise<void> {

const env = { $fs: fileSystemMapper, $import: initialize$import({ fs: fileSystemMapper, fetch }) }

const module = (await import(closestConfigPath)) as InlangConfigModule
const module = (await $import(closestConfigPath)) as InlangConfigModule
console.log("module", module)

const config = await setupConfig({ module, env })

const loadResources = async () => {
Expand Down Expand Up @@ -111,8 +114,6 @@ async function main(args: { context: vscode.ExtensionContext }): Promise<void> {
),
)

console.log("inlang extension started")

const documentSelectors = [
{ language: "javascript", pattern: "!**/inlang.config.js" },
...(state().config.ideExtension?.documentSelectors || []), // an empty array as fallback
Expand Down
63 changes: 63 additions & 0 deletions source-code/ide-extension/src/utils/$import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import path from "node:path"
import ts from "typescript"
import * as vscode from "vscode"
import fsExtra from "fs-extra"

// Transpile code to CommonJS using TypeScript Compiler API
function transpileCode(code: string): string {

This comment has been minimized.

Copy link
@samuelstroschein

samuelstroschein May 15, 2023

Member

wow that's so easy

const compilerOptions: ts.CompilerOptions = {
module: ts.ModuleKind.CommonJS,
target: ts.ScriptTarget.ES2015,
}

const result = ts.transpileModule(code, { compilerOptions })
return result.outputText
}

// Custom $import function
export const $import = async (modulePath: string): Promise<any> => {
// Read the code from the module path
const code = await vscode.workspace.fs.readFile(vscode.Uri.file(modulePath))

This comment has been minimized.

Copy link
@samuelstroschein

samuelstroschein May 15, 2023

Member

Where are you fetching the module path?

If someone imports from JSDelivr, you need to fetch the file, write to disk and then readFile. Otherwise, the file (obviously) doesn't exist.

https://github.com/inlang/inlang/blob/6a7fd3f8a93052770047f041a952b0cf03a400ee/source-code/core/src/environment/%24import.ts#L74-L77

This comment has been minimized.

Copy link
@felixhaeberle

felixhaeberle May 15, 2023

Author Contributor

This comment has been minimized.

Copy link
@samuelstroschein

samuelstroschein May 15, 2023

Member

This $import function is only being used for reading the inlang.config.js at the moment.

That is wrong.

Environment-related properties/functions like $import are passed down to code that is executed within the inlang.config.js file. Not to import the config module.

You need to use your modified $import for the environment in https://github.com/inlang/inlang/blob/6a7fd3f8a93052770047f041a952b0cf03a400ee/source-code/ide-extension/src/main.ts#LL79C1-L79C1 as a replacement for initialize$import. Like initialize$import, you need to specify how code should be imported:

  1. If HTTP import -> fetch the file
  2. Transpile the file to CJS
  3. Use require to "import" the file.

You should use a regular import in https://github.com/inlang/inlang/blob/6a7fd3f8a93052770047f041a952b0cf03a400ee/source-code/ide-extension/src/main.ts#L81. Esbuild will transpile the import call into a require call. (esbuild can't transpile dynamically imported code as it happens in the inlang.config.js file. That's why you need to manually "patch" $import to transpile on the fly.)


There seems to be a misconception about the inlang environment. Please elaborate so that we can update the docs at https://inlang.com/documentation/inlang-environment.

This comment has been minimized.

Copy link
@felixhaeberle

felixhaeberle May 15, 2023

Author Contributor

@samuelstroschein Thank you for your help!

So in short terms I have to duplicate all the behavior in https://github.com/inlang/inlang/blob/6a7fd3f8a93052770047f041a952b0cf03a400ee/source-code/core/src/environment/%24import.ts by modifying it to dynamically transpile to cjs?

This comment has been minimized.

Copy link
@samuelstroschein

samuelstroschein May 15, 2023

Member

@felixhaeberle Correct!

Can you open a PR that improves the documentation of what the inlang environment env does?

This comment has been minimized.

Copy link
@felixhaeberle

felixhaeberle May 15, 2023

Author Contributor

// Transpile the code to CommonJS
const transpiledCode = transpileCode(code.toString())

console.log("transpiledCode", transpiledCode)

// Create the .inlang folder if it doesn't exist
const inlangFolder = ".inlang"

This comment has been minimized.

Copy link
@samuelstroschein

samuelstroschein May 15, 2023

Member

should be added to .gitignore right?

Besides, I think that vscode offers "virtual files" https://code.visualstudio.com/api/extension-guides/virtual-documents. We might not need to create files that are visible to the end user.

const rootFolder = vscode.workspace.workspaceFolders?.[0]

if (!rootFolder) {
// Handle root folder not found
console.error("No workspace folder found.")
return undefined
}

const folderUri = rootFolder.uri.with({ path: path.join(rootFolder.uri.path, inlangFolder) })

try {
await vscode.workspace.fs.createDirectory(folderUri)
} catch (error) {
// Handle folder creation error
console.error("Failed to create .inlang folder:", error)
return undefined
}

// Generate a random file name
const tempFileName = `${Math.random().toString(36).slice(7)}.cjs`
const tempFilePath = folderUri.with({ path: path.join(folderUri.path, tempFileName) })

// Write the transpiled code to a temporary file
await vscode.workspace.fs.writeFile(tempFilePath, Buffer.from(transpiledCode))

// Use require to load the transpiled code
// eslint-disable-next-line @typescript-eslint/no-var-requires
const module = require(tempFilePath.fsPath)

// Clean up the temporary file and folder
await vscode.workspace.fs.delete(tempFilePath)
await fsExtra.remove(folderUri.fsPath)

return module
}

0 comments on commit 6994510

Please sign in to comment.