Skip to content

Commit

Permalink
feat: spawn main function as entrypoint in the entrypoint file (#574)
Browse files Browse the repository at this point in the history
* feat: spawn `main` function as entrypoint in the entrypoint file

* update snapshots

* update snapshots

* fix missing "use strict"

* update snapshot

* support async main

* add detail of composite were ok

* update snapshots

* update snapshots and loader
  • Loading branch information
leanmendoza authored May 16, 2023
1 parent 561a19a commit 7b905bd
Show file tree
Hide file tree
Showing 25 changed files with 151 additions and 99 deletions.
2 changes: 1 addition & 1 deletion packages/@dcl/playground-assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"scripts": {
"build": "npm run build-playground && node_modules/.bin/api-extractor run",
"build-local": "npm run build-playground && node_modules/.bin/api-extractor run --local --verbose --diagnostics",
"build-playground": "./../sdk-commands/dist/index.js build --production --emitDeclaration"
"build-playground": "./../sdk-commands/dist/index.js build --production --emitDeclaration --customEntryPoint"
},
"tsdoc": {
"tsdocFlavor": "AEDoc"
Expand Down
4 changes: 2 additions & 2 deletions packages/@dcl/sdk-commands/src/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export async function buildScene(options: Options, project: SceneProject) {

const watch = !!options.args['--watch']

const { sceneJson } = await bundleProject(
const { sceneJson, inputs } = await bundleProject(
options.components,
{
workingDirectory: project.workingDirectory,
Expand All @@ -84,6 +84,6 @@ export async function buildScene(options: Options, project: SceneProject) {
options.components.analytics.track('Build scene', {
projectHash: await b64HashingFunction(project.workingDirectory),
coords,
isWorkspace: false
isWorkspace: inputs.length > 1
})
}
122 changes: 80 additions & 42 deletions packages/@dcl/sdk-commands/src/logic/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@ export type CompileOptions = {

const MAX_STEP = 2

/**
* Generate the entry-point code for a given original entry-point
* @param entrypointPath - file to be imported as original entry point
* @param forceCustomExport
* @returns the Typescript code
*/

function getEntrypointCode(entrypointPath: string, forceCustomExport: boolean) {
if (forceCustomExport) return `;"use strict";export * from '${entrypointPath}'`

return `// BEGIN AUTO GENERATED CODE "~sdk/scene-entrypoint"
"use strict";
import * as entrypoint from '${entrypointPath}'
import { engine } from '@dcl/sdk/ecs'
import * as sdk from '@dcl/sdk'
if ((entrypoint as any).main !== undefined) {
function _INTERNAL_startup_system() {
const maybePromise = (entrypoint as any).main()
if (maybePromise && typeof maybePromise === 'object' && typeof (maybePromise as unknown as Promise<unknown>).then === 'function') {
maybePromise.catch(console.error)
}
engine.removeSystem(_INTERNAL_startup_system)
}
engine.addSystem(_INTERNAL_startup_system, Infinity)
}
export * from '@dcl/sdk'
export * from '${entrypointPath}'
`
}

export async function bundleProject(components: BundleComponents, options: CompileOptions, sceneJson: Scene) {
const tsconfig = path.join(options.workingDirectory, 'tsconfig.json')

Expand All @@ -58,24 +90,44 @@ export async function bundleProject(components: BundleComponents, options: Compi
throw new CliError(`File ${tsconfig} must exist to compile the Typescript project`)
}

const input = globSync(options.single ?? 'src/index.ts', { cwd: options.workingDirectory, absolute: true }) // entryPoints.map((item) => item.dest)
const entrypointSource = options.single ?? 'src/index.ts'
const entrypoints = globSync(entrypointSource, { cwd: options.workingDirectory, absolute: true })

/* istanbul ignore if */
if (!input.length) throw new CliError(`There are no input files to build: ${options.single ?? 'src/index.ts'}`)
if (!entrypoints.length) throw new CliError(`There are no input files to build: ${entrypointSource}`)

// const output = !options.single ? sceneJson.main : options.single.replace(/\.ts$/, '.js')
// const outfile = path.join(options.workingDirectory, output)
const inputs: { entrypoint: string; outputFile: string }[] = options.single
? entrypoints.map((entrypoint) => ({ entrypoint, outputFile: entrypoint.replace(/\.ts$/, '.js') }))
: [{ entrypoint: entrypoints[0], outputFile: sceneJson.main }]

for (const input of inputs) {
await bundleSingleProject(components, {
...options,
tsconfig,
...input
})
}

const output = !options.single ? sceneJson.main : options.single.replace(/\.ts$/, '.js')
const outfile = path.join(options.workingDirectory, output)
return { sceneJson, inputs }
}

printProgressStep(components.logger, `Bundling file ${colors.bold(input.join(','))}`, 1, MAX_STEP)
type SingleProjectOptions = CompileOptions & {
tsconfig: string
entrypoint: string
outputFile: string
}

export async function bundleSingleProject(components: BundleComponents, options: SingleProjectOptions) {
printProgressStep(components.logger, `Bundling file ${colors.bold(options.entrypoint)}`, 1, MAX_STEP)

const context = await esbuild.context({
entryPoints: input,
bundle: true,
platform: 'browser',
format: 'cjs',
preserveSymlinks: false,
outfile: input.length > 1 ? undefined : outfile,
outdir: input.length > 1 ? path.dirname(outfile) : undefined,
outfile: options.outputFile,
allowOverwrite: false,
sourcemap: options.production ? 'external' : 'inline',
minify: options.production,
Expand All @@ -88,40 +140,42 @@ export async function bundleProject(components: BundleComponents, options: Compi
target: 'es2020',
external: ['~system/*', '@dcl/inspector', '@dcl/inspector/*' /* ban importing the inspector from the SDK */],
// convert filesystem paths into file:// to enable VSCode debugger
sourceRoot: pathToFileURL(path.dirname(outfile)).toString(),
sourceRoot: pathToFileURL(path.dirname(options.outputFile)).toString(),
define: {
document: 'undefined',
window: 'undefined',
DEBUG: options.production ? 'false' : 'true',
'globalThis.DEBUG': options.production ? 'false' : 'true',
'process.env.NODE_ENV': JSON.stringify(options.production ? 'production' : 'development')
},
tsconfig: path.join(options.workingDirectory, 'tsconfig.json'),
tsconfig: options.tsconfig,
supported: {
'import-assertions': false,
'import-meta': false,
'dynamic-import': false,
hashbang: false
},
plugins: [entryPointLoader(components, input, options), compositeLoader(components, options)]
logOverride: {
'import-is-undefined': 'silent'
},
plugins: [compositeLoader(components, options)],
stdin: {
contents: getEntrypointCode(options.entrypoint, options.customEntryPoint),
resolveDir: path.dirname(options.entrypoint),
sourcefile: path.basename(options.entrypoint) + '.entry-point.ts',
loader: 'ts'
}
})

/* istanbul ignore if */
if (options.watch) {
await context.watch({})

printProgressInfo(components.logger, `Bundle saved ${colors.bold(output)}`)
printProgressInfo(components.logger, `Bundle saved ${colors.bold(options.outputFile)}`)
} else {
try {
const ctx = await context.rebuild()
printProgressInfo(
components.logger,
`Bundle saved ${colors.bold(
Object.keys(ctx.metafile.outputs)
.filter((_) => _.endsWith('.js'))
.join(',') || outfile
)}`
)
await context.rebuild()
printProgressInfo(components.logger, `Bundle saved ${colors.bold(options.outputFile)}`)
} catch (err: any) {
/* istanbul ignore next */
throw new CliError(err.toString())
Expand All @@ -133,8 +187,6 @@ export async function bundleProject(components: BundleComponents, options: Compi
if (options.watch) printProgressInfo(components.logger, `The compiler is watching for changes`)

await runTypeChecker(components, options)

return { context, sceneJson }
}

function runTypeChecker(components: BundleComponents, options: CompileOptions) {
Expand Down Expand Up @@ -176,10 +228,11 @@ function runTypeChecker(components: BundleComponents, options: CompileOptions) {
return typeCheckerFuture
}

function compositeLoader(components: BundleComponents, options: CompileOptions): esbuild.Plugin {
function compositeLoader(components: BundleComponents, options: SingleProjectOptions): esbuild.Plugin {
let shouldReload = true
let contents = `export const compositeFromLoader = {}` // default exports nothing
let watchFiles: string[] = [] // no files to watch
let lastBuiltSuccessful = false

return {
name: 'composite-loader',
Expand Down Expand Up @@ -210,7 +263,10 @@ function compositeLoader(components: BundleComponents, options: CompileOptions):
components.logger,
'Some composites are not included because of errors while compiling them. There can be unexpected behavior in the scene, check the errors and try to fix them.'
)
} else if (!lastBuiltSuccessful) {
components.logger.log('Composites built without errors.')
}
lastBuiltSuccessful = !data.withErrors
}
shouldReload = false
}
Expand All @@ -224,21 +280,3 @@ function compositeLoader(components: BundleComponents, options: CompileOptions):
}
}
}

function entryPointLoader(components: BundleComponents, inputs: string[], options: CompileOptions): esbuild.Plugin {
const escapedInputs = inputs.map(($) => $.replace(/\\/g, '\\\\'))
const filter = new RegExp(`(${escapedInputs.join('|')})`)
return {
name: 'entry-point-loader',
setup(build) {
build.onLoad({ filter }, async (args) => {
const exportSdk = options.customEntryPoint ? '' : `;export * from '@dcl/sdk';`
const contents = exportSdk + (await components.fs.readFile(args.path))
return {
loader: 'ts',
contents
}
})
}
}
}
5 changes: 4 additions & 1 deletion packages/@dcl/sdk/src/composite-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ export const compositeProvider: Composite.Provider = {
const fromLoader = compositeFromLoader[src]
if (fromLoader) {
try {
if (src.endsWith('.bin') && fromLoader instanceof Uint8Array) {
if (fromLoader instanceof Uint8Array) {
const composite = Composite.fromBinary(fromLoader)
composites.push({ src, composite })
} else if (typeof fromLoader === 'string') {
const composite = Composite.fromJson(JSON.parse(fromLoader))
composites.push({ src, composite })
} else if (typeof fromLoader === 'object') {
const composite = Composite.fromJson(fromLoader)
composites.push({ src, composite })
}
} catch (err) {
console.error(err)
Expand Down
1 change: 1 addition & 0 deletions test/build-ecs/sourcemaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function assertFilesExist(map: BasicSourceMapConsumer) {

// TODO: what should we do with virtual files?
if (file.endsWith('sdk-composite:all-composites')) continue
if (file.endsWith('entry-point.ts')) continue

const fileExist = existsSync(fileURLToPath(file)) ? file : 'does not exit'
expect(fileExist).toBe(file)
Expand Down
2 changes: 1 addition & 1 deletion test/sdk-commands/commands/build/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('build command', () => {
.mockResolvedValue({ kind: 'scene', scene: {} as any, workingDirectory: process.cwd() })
jest.spyOn(projectValidation, 'needsDependencies').mockResolvedValue(false)
const sceneJson = { scene: { base: '0,0' } } as any
const tsBuildSpy = jest.spyOn(dclCompiler, 'bundleProject').mockResolvedValue({ sceneJson, context: null as any })
const tsBuildSpy = jest.spyOn(dclCompiler, 'bundleProject').mockResolvedValue({ sceneJson, inputs: [] })

await build.main({
args: { _: [], '--watch': false, '--production': true },
Expand Down
5 changes: 5 additions & 0 deletions test/snapshots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ describe('Runs the snapshots', () => {
path.resolve('test/snapshots'),
ENV
)
itExecutes(
`npm run build -- --production --ignoreComposite "--single=production-bundles/with-main-function.ts"`,
path.resolve('test/snapshots'),
ENV
)
itExecutes(
`npm run build -- --customEntryPoint --ignoreComposite "--single=development-bundles/*.ts"`,
path.resolve('test/snapshots'),
Expand Down
8 changes: 4 additions & 4 deletions test/snapshots/development-bundles/static-scene.test.ts.crdt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SCENE_COMPILED_JS_SIZE_PROD=374.2k bytes
SCENE_COMPILED_JS_SIZE_PROD=374.4k bytes
THE BUNDLE HAS SOURCEMAPS
(start empty vm 0.21.0-3680274614.commit-1808aa1)
OPCODES ~= 0k
Expand All @@ -10,8 +10,8 @@ EVAL test/snapshots/development-bundles/static-scene.test.js
REQUIRE: ~system/Testing
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
OPCODES ~= 32k
MALLOC_COUNT = 9961
OPCODES ~= 31k
MALLOC_COUNT = 9960
ALIVE_OBJS_DELTA ~= 1.84k
CALL onStart()
main.crdt: PUT_COMPONENT e=0x200 c=1 t=0 data={"position":{"x":5.880000114440918,"y":2.7916901111602783,"z":7.380000114440918},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0}
Expand Down Expand Up @@ -56,4 +56,4 @@ CALL onUpdate(0.1)
OPCODES ~= 2k
MALLOC_COUNT = -3
ALIVE_OBJS_DELTA ~= 0.00k
MEMORY_USAGE_COUNT ~= 982.42k bytes
MEMORY_USAGE_COUNT ~= 982.62k bytes
2 changes: 1 addition & 1 deletion test/snapshots/development-bundles/testing-fw.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { withRenderer } from '../helpers/with-renderer'
import { assert } from '../helpers/assertions'
export * from '@dcl/sdk'

withRenderer((engine) => {
export const onServerUpdate = withRenderer((engine) => {
// this helper creates a second engine and prints all the messages to emulate
// the renderer counterpart of the CRDT
const Transform = components.Transform(engine)
Expand Down
6 changes: 3 additions & 3 deletions test/snapshots/development-bundles/testing-fw.test.ts.crdt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SCENE_COMPILED_JS_SIZE_PROD=374.7k bytes
SCENE_COMPILED_JS_SIZE_PROD=374.9k bytes
THE BUNDLE HAS SOURCEMAPS
(start empty vm 0.21.0-3680274614.commit-1808aa1)
OPCODES ~= 0k
Expand All @@ -11,7 +11,7 @@ EVAL test/snapshots/development-bundles/testing-fw.test.js
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
OPCODES ~= 32k
MALLOC_COUNT = 10061
MALLOC_COUNT = 10068
ALIVE_OBJS_DELTA ~= 1.88k
CALL onStart()
LOG: ["Adding one to position.y=0"]
Expand Down Expand Up @@ -64,4 +64,4 @@ CALL onUpdate(0.1)
OPCODES ~= 4k
MALLOC_COUNT = -40
ALIVE_OBJS_DELTA ~= -0.01k
MEMORY_USAGE_COUNT ~= 982.16k bytes
MEMORY_USAGE_COUNT ~= 982.57k bytes
2 changes: 1 addition & 1 deletion test/snapshots/development-bundles/two-way-crdt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { withRenderer } from '../helpers/with-renderer'
import { assert } from '../helpers/assertions'
export * from '@dcl/sdk'

withRenderer((engine) => {
export const onServerUpdate = withRenderer((engine) => {
// this helper creates a second engine and prints all the messages to emulate
// the renderer counterpart of the CRDT
const Transform = components.Transform(engine)
Expand Down
6 changes: 3 additions & 3 deletions test/snapshots/development-bundles/two-way-crdt.test.ts.crdt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SCENE_COMPILED_JS_SIZE_PROD=374.7k bytes
SCENE_COMPILED_JS_SIZE_PROD=374.9k bytes
THE BUNDLE HAS SOURCEMAPS
(start empty vm 0.21.0-3680274614.commit-1808aa1)
OPCODES ~= 0k
Expand All @@ -11,7 +11,7 @@ EVAL test/snapshots/development-bundles/two-way-crdt.test.js
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
OPCODES ~= 32k
MALLOC_COUNT = 10061
MALLOC_COUNT = 10068
ALIVE_OBJS_DELTA ~= 1.88k
CALL onStart()
LOG: ["Adding one to position.y=0"]
Expand Down Expand Up @@ -64,4 +64,4 @@ CALL onUpdate(0.1)
OPCODES ~= 4k
MALLOC_COUNT = -40
ALIVE_OBJS_DELTA ~= -0.01k
MEMORY_USAGE_COUNT ~= 982.16k bytes
MEMORY_USAGE_COUNT ~= 982.58k bytes
3 changes: 1 addition & 2 deletions test/snapshots/helpers/with-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Transport } from '@dcl/ecs'
import { IEngine, Engine } from '@dcl/ecs/dist/engine'

let called = false
declare const module: any

export function withRenderer(cb: (engine: IEngine) => void) {
if (called) throw new Error('Only call withRenderer once')
Expand All @@ -26,7 +25,7 @@ export function withRenderer(cb: (engine: IEngine) => void) {
engine.addTransport(rendererTransport)

cb(engine)
;(module as any).exports.onServerUpdate = async function (data: Uint8Array) {
return async function (data: Uint8Array) {
if (rendererTransport.onmessage) {
rendererTransport.onmessage(data)

Expand Down
2 changes: 1 addition & 1 deletion test/snapshots/production-bundles/append-value-crdt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export * from '@dcl/sdk'

const entity = 512 as Entity

withRenderer((engine) => {
export const onServerUpdate = withRenderer((engine) => {
const PointerEventsResult = components.PointerEventsResult(engine)

let count = 0
Expand Down
Loading

0 comments on commit 7b905bd

Please sign in to comment.