11 Commits

Author SHA1 Message Date
254c5ab48f Merge pull request #10 from FatttSnake/refactor/compiler
Fix bug in compiler handling of dependency imports
2024-09-14 00:40:10 +08:00
89cf48e449 Refactor(compiler): Optimize the logic of resolve import
Automatically externalize packages defined in importMap.
Solve the problems caused by multiple instances of packages such as React.

Closes #9
2024-09-13 16:16:01 +08:00
d6d5cd927c Refactor(Transform): Remove auto add react import when transform 2024-09-13 16:15:58 +08:00
72ab390756 Refactor(files-util): Optimize the regular expression to match "import react" 2024-09-13 16:15:55 +08:00
92115d3faa Merge pull request #8 from FatttSnake/prepare/1.0.0
Update version from 1.0.0 to 1.0.1-SNAPSHOT
2024-09-13 15:27:19 +08:00
21eaee22f7 Build(package.json): Update version from 1.0.0 to 1.0.1-SNAPSHOT 2024-09-13 15:24:16 +08:00
44e32ce4f7 Merge pull request #7 from FatttSnake/6-unable-to-automatically-resolve-multi-level-dependency-paths
Fix(compiler): Fix multi-level dependency path compilation error BUG
2024-09-10 11:37:43 +08:00
6302ec6ab3 Fix(compiler): Fix multi-level dependency path compilation error BUG
Close #6
2024-09-10 11:36:40 +08:00
1c45e7250b Merge pull request #5 from FatttSnake/4-variable-pollution
Fixed code pollution bug
2024-09-09 17:24:20 +08:00
51ee15749e Fix(Preview): Fixed code pollution bug
Methods and variables in base and main code are isolated to solve the problem of code pollution. Close #4
2024-09-09 17:22:42 +08:00
bba90c6783 Build(package.json): Update version from 1.0.0-SNAPSHOT to 1.0.1-SNAPSHOT 2024-09-09 16:55:38 +08:00
9 changed files with 126 additions and 158 deletions

10
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "oxygen-ui",
"version": "1.0.0-SNAPSHOT",
"version": "1.0.1-SNAPSHOT",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "oxygen-ui",
"version": "1.0.0-SNAPSHOT",
"version": "1.0.1-SNAPSHOT",
"dependencies": {
"@ant-design/icons": "^5.3.7",
"@dnd-kit/core": "^6.1.0",
@@ -2153,9 +2153,9 @@
}
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"dev": true,
"bin": {
"acorn": "bin/acorn"

View File

@@ -2,7 +2,7 @@
"name": "oxygen-ui",
"private": true,
"type": "module",
"version": "1.0.0",
"version": "1.0.1-SNAPSHOT",
"description": "Oxygen Toolbox browser version",
"author": {
"name": "FatttSnake",

View File

@@ -32,7 +32,7 @@ const Preview = ({
Compiler.compile(files, importMap, entryPoint)
.then((result) => {
setCompiledCode(
`${preExpansionCode}\n${result.outputFiles[0].text}\n${postExpansionCode}`
`(()=>{${preExpansionCode}})();\n(()=>{${result.outputFiles[0].text}})();\n(()=>{${postExpansionCode}})();`
)
setErrorMsg('')
})

View File

@@ -2,7 +2,7 @@ import MonacoEditor from '@monaco-editor/react'
import { Loader } from 'esbuild-wasm'
import '@/components/Playground/Output/Transform/transform.scss'
import { IFile, ITheme } from '@/components/Playground/shared'
import { cssToJs, jsonToJs, addReactImport } from '@/components/Playground/files'
import { cssToJs, jsonToJs } from '@/components/Playground/files'
import Compiler from '@/components/Playground/compiler'
import { MonacoEditorConfig } from '@/components/Playground/CodeEditor/Editor/monacoConfig'
@@ -16,12 +16,7 @@ const Transform = ({ file, theme }: OutputProps) => {
const [errorMsg, setErrorMsg] = useState('')
const compile = (code: string, loader: Loader) => {
let _code = code
if (['jsx', 'tsx'].includes(loader)) {
_code = addReactImport(code)
}
Compiler?.transform(_code, loader)
Compiler?.transform(code, loader)
.then((value) => {
setCompiledCode(value.code)
setErrorMsg('')

View File

@@ -1,8 +1,8 @@
import esbuild, { Loader, OnLoadArgs, Plugin, PluginBuild } from 'esbuild-wasm'
import localforage from 'localforage'
import axios from 'axios'
import { IFiles, IImportMap } from '@/components/Playground/shared'
import { cssToJs, jsonToJs, addReactImport } from '@/components/Playground/files'
import { IFile, IFiles, IImportMap } from '@/components/Playground/shared'
import { addReactImport, cssToJs, jsonToJs } from '@/components/Playground/files'
class Compiler {
private init = false
@@ -61,7 +61,7 @@ class Compiler {
format: 'esm',
metafile: true,
write: false,
plugins: [this.fileResolverPlugin(files, importMap, entryPoint)]
plugins: [this.fileResolverPlugin(files, importMap)]
})
})
@@ -69,55 +69,16 @@ class Compiler {
void esbuild.stop()
}
private fileResolverPlugin = (
files: IFiles,
importMap: IImportMap,
entryPoint: string
): Plugin => {
return {
private fileResolverPlugin = (files: IFiles, importMap: IImportMap): Plugin => ({
name: 'file-resolver-plugin',
setup: (build: PluginBuild) => {
build.onResolve({ filter: /.*/ }, (args: esbuild.OnResolveArgs) => {
if (entryPoint === args.path) {
if (args.kind === 'entry-point') {
return {
namespace: 'OxygenToolbox',
namespace: 'oxygen',
path: args.path
}
}
if (args.path.startsWith('./') && files[args.path.substring(2)]) {
return {
namespace: 'OxygenToolbox',
path: args.path.substring(2)
}
}
if (args.path.startsWith('./') && files[`${args.path.substring(2)}.tsx`]) {
return {
namespace: 'OxygenToolbox',
path: `${args.path.substring(2)}.tsx`
}
}
if (args.path.startsWith('./') && files[`${args.path.substring(2)}.jsx`]) {
return {
namespace: 'OxygenToolbox',
path: `${args.path.substring(2)}.jsx`
}
}
if (args.path.startsWith('./') && files[`${args.path.substring(2)}.ts`]) {
return {
namespace: 'OxygenToolbox',
path: `${args.path.substring(2)}.ts`
}
}
if (args.path.startsWith('./') && files[`${args.path.substring(2)}.js`]) {
return {
namespace: 'OxygenToolbox',
path: `${args.path.substring(2)}.js`
}
}
if (/\.\/.*\.css/.test(args.path) && !args.resolveDir) {
throw Error(`Css '${args.path}' not found`)
}
if (/^https?:\/\/.*/.test(args.path)) {
return {
namespace: 'default',
@@ -126,25 +87,50 @@ class Compiler {
}
if (
args.path.includes('./') ||
args.path.includes('../') ||
args.path.startsWith('/')
args.path.startsWith('./') &&
(!args.resolveDir.length || args.resolveDir in files)
) {
const suffix = ['', '.tsx', '.jsx', '.ts', '.js'].find((suffix) => {
return files[`${args.path.substring(2)}${suffix}`]
})
if (suffix !== undefined) {
return {
namespace: 'oxygen',
path: `${args.path.substring(2)}${suffix}`
}
}
}
if (['./', '../', '/'].some((prefix) => args.path.startsWith(prefix))) {
return {
namespace: 'default',
path: new URL(args.path, args.resolveDir.substring(1)).href
}
}
const path = importMap.imports[args.path]
let path = importMap.imports[args.path]
let tempPath = args.path
while (!path && tempPath.includes('/')) {
tempPath = tempPath.substring(0, tempPath.lastIndexOf('/'))
if (importMap.imports[tempPath]) {
const suffix = args.path.replace(tempPath, '')
const importUrl = new URL(importMap.imports[tempPath])
path = `${importUrl.origin}${importUrl.pathname}${suffix}${importUrl.search}`
}
}
if (!path) {
throw Error(`Import '${args.path}' not found in Import Map`)
}
const pathUrl = new URL(path)
const externals = pathUrl.searchParams.get('external')?.split(',') ?? []
Object.keys(importMap.imports).forEach((item) => {
if (!(item in externals)) {
externals.push(item)
}
})
pathUrl.searchParams.set('external', externals.join(','))
return {
namespace: 'default',
path
path: pathUrl.href
}
})
@@ -164,40 +150,28 @@ class Compiler {
}
})
build.onLoad({ namespace: 'oxygen', filter: /.*/ }, (args: OnLoadArgs) => {
let file: IFile | undefined
void ['', '.tsx', '.jsx', '.ts', '.js'].forEach((suffix) => {
file = file || files[`${args.path}${suffix}`]
})
if (file) {
return {
loader: (() => {
switch (file.language) {
case 'javascript':
return 'jsx'
default:
return 'tsx'
}
})(),
contents: addReactImport(file.value)
}
}
})
build.onLoad({ filter: /.*/ }, async (args: OnLoadArgs) => {
if (entryPoint === args.path) {
return {
loader: 'tsx',
contents: addReactImport(files[entryPoint].value)
}
}
if (files[args.path]) {
const contents = addReactImport(files[args.path].value)
if (args.path.endsWith('.jsx')) {
return {
loader: 'jsx',
contents
}
}
if (args.path.endsWith('.ts')) {
return {
loader: 'ts',
contents
}
}
if (args.path.endsWith('.js')) {
return {
loader: 'js',
contents
}
}
return {
loader: 'tsx',
contents
}
}
const cached = await this.fileCache.getItem<esbuild.OnLoadResult>(args.path)
if (cached) {
@@ -206,9 +180,9 @@ class Compiler {
const axiosResponse = await axios.get<string>(args.path)
const result: esbuild.OnLoadResult = {
loader: 'jsx',
loader: 'js',
contents: axiosResponse.data,
resolveDir: (axiosResponse.request as XMLHttpRequest).responseURL
resolveDir: args.path
}
await this.fileCache.setItem(args.path, result)
@@ -216,8 +190,7 @@ class Compiler {
return result
})
}
}
}
})
}
export default new Compiler()

View File

@@ -106,7 +106,7 @@ export const cssToJs = (file: IFile) => {
}
export const addReactImport = (code: string) => {
if (!/import\s+React/g.test(code)) {
if (!/^\s*import\s+React\s+/g.test(code)) {
return `import React from 'react';\n${code}`
}
return code

View File

@@ -27,7 +27,7 @@ const Execute = () => {
const output = result.outputFiles[0].text
setCompiledCode('')
setTimeout(() => {
setCompiledCode(`${output}\n${baseDist}`)
setCompiledCode(`(() => {${output}})();\n(() => {${baseDist}})();`)
}, 100)
})
.catch((reason) => {

View File

@@ -145,7 +145,7 @@ const Create = () => {
.compile(files, importMap, template.entryPoint)
.then((result) => {
const output = result.outputFiles[0].text
setCompiledCode(`${output}\n${baseDist}`)
setCompiledCode(`(() => {${output}})();\n(() => {${baseDist}})();`)
})
.catch((reason) => {
void message.error(`编译失败:${reason}`)

View File

@@ -34,7 +34,7 @@ const View = () => {
const output = result.outputFiles[0].text
setCompiledCode('')
setTimeout(() => {
setCompiledCode(`${output}\n${baseDist}`)
setCompiledCode(`(() => {${output}})();\n(() => {${baseDist}})();`)
}, 100)
})
.catch((reason) => {
@@ -49,7 +49,7 @@ const View = () => {
const dist = base64ToStr(toolVo.dist.data!)
setCompiledCode('')
setTimeout(() => {
setCompiledCode(`${dist}\n${baseDist}`)
setCompiledCode(`(() => {${dist}})();\n(() => {${baseDist}})();`)
}, 100)
} catch (e) {
void message.error('载入工具失败')