diff --git a/README.md b/README.md index 9c58eb5..722a450 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,9 @@ const projector = new ProjectorWindows('projector-windows/application.exe'); // Required skeleton. projector.skeleton = 'skeleton.zip'; +// File to create config file with. +projector.configFile = 'config.ini'; + // Optional custom icon. projector.iconFile = 'icon.ico'; @@ -73,7 +76,7 @@ projector.nestXtrasConfiguration = true; // Optionally fix Shockwave 3D Xtra InstalledDisplayDrivers reading. projector.patchShockwave3dInstalledDisplayDriversSize = true; -await projector.withFile('config.ini'); +await projector.write(); ``` ### Mac App @@ -86,6 +89,9 @@ const projector = new ProjectorMacApp('projector-macapp/application.app'); // Required skeleton. projector.skeleton = 'skeleton.zip'; +// File to create config file with. +projector.configFile = 'config.ini'; + // Optional custom icon. projector.iconFile = 'icon.icns'; @@ -124,7 +130,7 @@ projector.nestXtrasContents = true; // Optionally use Intel-only skeleton. // projector.intel = true; -await projector.withFile('config.ini'); +await projector.write(); ``` ## Bundle @@ -139,7 +145,10 @@ const bundle = new BundleWindows('bundle-windows/application.exe'); // Use projector property to set options. bundle.projector.skeleton = 'skeleton.zip'; -await bundle.withFile('config.ini', async b => { +// File to create config file with. +bundle.projector.configFile = 'config.ini'; + +await bundle.write(async b => { // Add resources in callback. await b.copyResource('movie.dir', 'movie.dir'); }); @@ -155,7 +164,10 @@ const bundle = new BundleMacApp('bundle-macapp/application.app'); // Use projector property to set options. bundle.projector.skeleton = 'skeleton.zip'; -await bundle.withFile('config.ini', async b => { +// File to create config file with. +bundle.projector.configFile = 'config.ini'; + +await bundle.write(async b => { // Add resources in callback. await b.copyResource('movie.dir', 'movie.dir'); }); diff --git a/src/bundle.test.ts b/src/bundle.test.ts index e9206c8..bee5dba 100644 --- a/src/bundle.test.ts +++ b/src/bundle.test.ts @@ -64,7 +64,8 @@ void describe('bundle', () => { const b = new BundleDummy(dest); b.projector.skeleton = fixtureFile('dummy.zip'); - await b.withFile(fixtureFile('config.ini.crlf.bin')); + b.projector.configFile = fixtureFile('config.ini.crlf.bin'); + await b.write(); }); void it('resources', async () => { @@ -109,7 +110,8 @@ void describe('bundle', () => { const b = new BundleDummy(dest); b.projector.skeleton = fixtureFile('dummy.zip'); - await b.withFile(fixtureFile('config.ini.crlf.bin'), async p => { + b.projector.configFile = fixtureFile('config.ini.crlf.bin'); + await b.write(async p => { await p.copyResource('resources0', resources); await p.copyResource('resources1', resources, { @@ -252,7 +254,8 @@ void describe('bundle', () => { const b = new BundleDummy(dest); b.projector.skeleton = fixtureFile('dummy.zip'); - await b.withFile(fixtureFile('config.ini.crlf.bin'), async p => { + b.projector.configFile = fixtureFile('config.ini.crlf.bin'); + await b.write(async p => { await p.createResourceFile('d/b.txt', 'beta'); // Merge contents at root of resources. diff --git a/src/bundle.ts b/src/bundle.ts index 4650373..3c31e99 100644 --- a/src/bundle.ts +++ b/src/bundle.ts @@ -3,7 +3,6 @@ import { chmod, lstat, mkdir, - readFile, readlink, stat, symlink, @@ -133,28 +132,16 @@ export abstract class Bundle { } /** - * Open output with file. - * - * @param configFile Config file. + * Open output. */ - public async openFile(configFile: string | null) { - const configData = configFile ? await readFile(configFile) : null; - await this.openData(configData); - } - - /** - * Open output with data. - * - * @param configData Config data. - */ - public async openData(configData: Readonly | null) { + public async open() { if (this._isOpen) { throw new Error('Already open'); } await this._checkOutput(); this._closeQueue.clear(); - await this._openData(configData); + await this._open(); this._isOpen = true; } @@ -175,34 +162,14 @@ export abstract class Bundle { } /** - * Write out projector with player and file. + * Write out the bundle. * Has a callback to write out the resources. * - * @param configFile Config file. * @param func Async function. * @returns Return value of the async function. */ - public async withFile( - configFile: string | null, - func: ((self: this) => Promise) | null = null - ) { - const configData = configFile ? await readFile(configFile) : null; - return this.withData(configData, func); - } - - /** - * Write out projector with player and data. - * Has a callback to write out the resources. - * - * @param configData Config data. - * @param func Async function. - * @returns Return value of the async function. - */ - public async withData( - configData: Readonly | null, - func: ((self: this) => Promise) | null = null - ) { - await this.openData(configData); + public async write(func: ((self: this) => Promise) | null = null) { + await this.open(); try { return func ? await func.call(this, this) : null; } finally { @@ -580,12 +547,10 @@ export abstract class Bundle { } /** - * Open output with data. - * - * @param configData Config data. + * Open output. */ - protected async _openData(configData: Readonly | null) { - await this.projector.withData(configData); + protected async _open() { + await this.projector.write(); } /** diff --git a/src/bundle/mac/app.test.ts b/src/bundle/mac/app.test.ts index 9201787..fa6e753 100644 --- a/src/bundle/mac/app.test.ts +++ b/src/bundle/mac/app.test.ts @@ -27,15 +27,13 @@ void describe('bundle/mac/app', () => { const b = new BundleMacApp(dest); b.projector.skeleton = await getSkeleton(); - await b.withFile( - fixtureFile('config.ini.lf.bin'), - async b => { - await b.copyResource( - 'movie.dir', - fixtureFile('dir7.dir') - ); - } - ); + b.projector.configFile = fixtureFile('config.ini.lf.bin'); + await b.write(async b => { + await b.copyResource( + 'movie.dir', + fixtureFile('dir7.dir') + ); + }); }); void it('complex', async () => { @@ -45,6 +43,7 @@ void describe('bundle/mac/app', () => { const b = new BundleMacApp(dest); const p = b.projector; p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.lingoFile = fixtureFile('lingo.ini.lf.bin'); p.splashImageFile = fixtureFile('splash.pict'); p.iconFile = fixtureFile('icon.icns'); @@ -57,15 +56,12 @@ void describe('bundle/mac/app', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '': null }; - await b.withFile( - fixtureFile('config.ini.lf.bin'), - async b => { - await b.copyResource( - 'movie.dir', - fixtureFile('dir7.dir') - ); - } - ); + await b.write(async b => { + await b.copyResource( + 'movie.dir', + fixtureFile('dir7.dir') + ); + }); }); }); } diff --git a/src/bundle/windows.test.ts b/src/bundle/windows.test.ts index 87fddfe..69d4c7c 100644 --- a/src/bundle/windows.test.ts +++ b/src/bundle/windows.test.ts @@ -31,15 +31,13 @@ void describe('bundle/windows', () => { const b = new BundleWindows(dest); b.projector.skeleton = await getSkeleton(); - await b.withFile( - fixtureFile('config.ini.crlf.bin'), - async b => { - await b.copyResource( - 'movie.dir', - fixtureFile('dir7.dir') - ); - } - ); + b.projector.configFile = fixtureFile('config.ini.crlf.bin'); + await b.write(async b => { + await b.copyResource( + 'movie.dir', + fixtureFile('dir7.dir') + ); + }); }); void it('complex', async () => { @@ -49,6 +47,7 @@ void describe('bundle/windows', () => { const b = new BundleWindows(dest); const p = b.projector; p.skeleton = await getSkeleton(); + b.projector.configFile = fixtureFile('config.ini.crlf.bin'); p.lingoFile = fixtureFile('lingo.ini.crlf.bin'); p.splashImageFile = fixtureFile('splash.bmp'); p.nestXtrasConfiguration = true; @@ -60,15 +59,12 @@ void describe('bundle/windows', () => { patchShockwave3dInstalledDisplayDriversSize; p.iconFile = fixtureFile('icon.ico'); p.versionStrings = versionStrings; - await b.withFile( - fixtureFile('config.ini.crlf.bin'), - async b => { - await b.copyResource( - 'movie.dir', - fixtureFile('dir7.dir') - ); - } - ); + await b.write(async b => { + await b.copyResource( + 'movie.dir', + fixtureFile('dir7.dir') + ); + }); }); }); } diff --git a/src/projector.test.ts b/src/projector.test.ts index 2f54e08..47cbfd1 100644 --- a/src/projector.test.ts +++ b/src/projector.test.ts @@ -15,7 +15,8 @@ void describe('projector', () => { const p = new ProjectorDummy(dest); p.skeleton = 'dummy'; - await p.withFile(fixtureFile('config.ini.crlf.bin')); + p.configFile = fixtureFile('config.ini.crlf.bin'); + await p.write(); await copyFile(fixtureFile('dir7.dir'), pathJoin(dir, 'movie.dir')); }); @@ -26,8 +27,9 @@ void describe('projector', () => { const p = new ProjectorDummy(dest); p.skeleton = 'dummy'; + p.configFile = fixtureFile('config.ini.crlf.bin'); p.lingoFile = fixtureFile('lingo.ini.crlf.bin'); - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile(fixtureFile('dir7.dir'), pathJoin(dir, 'movie.dir')); }); @@ -38,8 +40,9 @@ void describe('projector', () => { const p = new ProjectorDummy(dest); p.skeleton = 'dummy'; + p.configFile = fixtureFile('config.ini.crlf.bin'); p.splashImageFile = fixtureFile('splash.bmp'); - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile(fixtureFile('dir7.dir'), pathJoin(dir, 'movie.dir')); }); @@ -50,9 +53,10 @@ void describe('projector', () => { const p = new ProjectorDummy(dest); p.skeleton = 'dummy'; + p.configFile = fixtureFile('config.ini.crlf.bin'); p.lingoFile = fixtureFile('lingo.ini.crlf.bin'); p.splashImageFile = fixtureFile('splash.bmp'); - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile(fixtureFile('dir7.dir'), pathJoin(dir, 'movie.dir')); }); diff --git a/src/projector.ts b/src/projector.ts index 6bbb985..232523a 100644 --- a/src/projector.ts +++ b/src/projector.ts @@ -85,6 +85,22 @@ export abstract class Projector { */ public skeleton: string | null = null; + /** + * Config data. + */ + public configData: + | Readonly + | string + | Readonly + | (() => Readonly | string | Readonly) + | (() => Promise | string | Readonly>) + | null = null; + + /** + * Config file. + */ + public configFile: string | null = null; + /** * Output path. */ @@ -185,6 +201,48 @@ export abstract class Projector { : pathJoin(dirname(this.path), this.xtrasName); } + /** + * Get config file data. + * + * @returns Config data or null. + */ + public async getConfigData() { + const {configData, configFile} = this; + if (configData) { + switch (typeof configData) { + case 'function': { + const d = await configData(); + if (typeof d === 'string') { + return new TextEncoder().encode(d); + } + if (Array.isArray(d)) { + return new TextEncoder().encode( + d.join(this.configNewline) + ); + } + return d as Readonly; + } + case 'string': { + return new TextEncoder().encode(configData); + } + default: { + // Fall through. + } + } + if (Array.isArray(configData)) { + return new TextEncoder().encode( + configData.join(this.configNewline) + ); + } + return configData as Readonly; + } + if (configFile) { + const d = await readFile(configFile); + return new Uint8Array(d.buffer, d.byteOffset, d.byteLength); + } + return null; + } + /** * Get splash image data if any specified, from data or file. * @@ -290,22 +348,9 @@ export abstract class Projector { } /** - * Write out projector with skeleton and config file. - * - * @param configFile Config file. - */ - public async withFile(configFile: string | null) { - await this.withData(configFile ? await readFile(configFile) : null); - } - - /** - * Write out projector with skeleton and config data. - * - * @param configData Config data. + * Write out the projector. */ - public async withData( - configData: Readonly | string | Readonly | null - ) { + public async write() { const {skeleton} = this; if (!skeleton) { throw new Error('No projector skeleton configured'); @@ -314,7 +359,7 @@ export abstract class Projector { await this._checkOutput(); await this._writeSkeleton(skeleton); await this._modifySkeleton(); - await this._writeConfig(configData); + await this._writeConfig(); await this._writeSplashImage(); await this._writeLingo(); } @@ -339,21 +384,9 @@ export abstract class Projector { /** * Write out the projector config file. - * - * @param configData Config data. */ - protected async _writeConfig( - configData: Readonly | string | Readonly | null - ) { - let data; - if (typeof configData === 'string') { - data = configData; - } else if (Array.isArray(data)) { - data = (configData as string[]).join(this.configNewline); - } else if (configData) { - data = configData as Readonly; - } - + protected async _writeConfig() { + const data = await this.getConfigData(); if (data) { const {configPath} = this; await mkdir(dirname(configPath), {recursive: true}); diff --git a/src/projector/mac/app.test.ts b/src/projector/mac/app.test.ts index ca8fa88..623f0bf 100644 --- a/src/projector/mac/app.test.ts +++ b/src/projector/mac/app.test.ts @@ -27,7 +27,8 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); - await p.withFile(fixtureFile('config.ini.lf.bin')); + p.configFile = fixtureFile('config.ini.lf.bin'); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -41,11 +42,12 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.includeXtras = { // eslint-disable-next-line @typescript-eslint/naming-convention '': null }; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -59,12 +61,13 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.includeXtras = { Scripting: null, // eslint-disable-next-line @typescript-eslint/naming-convention 'GIF Agent.xtra': null }; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -78,12 +81,13 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.includeXtras = { Scripting: 'Scripting RENAMED', // eslint-disable-next-line @typescript-eslint/naming-convention 'GIF Agent.xtra': 'GIF Agent RENAMED.xtra' }; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -97,8 +101,9 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.shockwave = true; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -112,12 +117,13 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.nestXtrasConfiguration = true; p.includeXtras = { // eslint-disable-next-line @typescript-eslint/naming-convention '': null }; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -132,12 +138,13 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.nestXtrasContents = true; p.includeXtras = { // eslint-disable-next-line @typescript-eslint/naming-convention '': null }; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -153,8 +160,9 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.intel = true; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -169,6 +177,7 @@ void describe('projector/mac/app', () => { const p = new ProjectorMacApp(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.lf.bin'); p.lingoFile = fixtureFile('lingo.ini.lf.bin'); p.splashImageFile = fixtureFile('splash.pict'); p.iconFile = fixtureFile('icon.icns'); @@ -181,7 +190,7 @@ void describe('projector/mac/app', () => { // eslint-disable-next-line @typescript-eslint/naming-convention '': null }; - await p.withFile(fixtureFile('config.ini.lf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), diff --git a/src/projector/windows.test.ts b/src/projector/windows.test.ts index dd7758b..0a3e6d0 100644 --- a/src/projector/windows.test.ts +++ b/src/projector/windows.test.ts @@ -31,7 +31,8 @@ void describe('projector/windows', () => { const p = new ProjectorWindows(dest); p.skeleton = await getSkeleton(); - await p.withFile(fixtureFile('config.ini.crlf.bin')); + p.configFile = fixtureFile('config.ini.crlf.bin'); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -45,13 +46,14 @@ void describe('projector/windows', () => { const p = new ProjectorWindows(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.crlf.bin'); p.includeXtras = { // eslint-disable-next-line @typescript-eslint/naming-convention '': null }; p.patchShockwave3dInstalledDisplayDriversSize = patchShockwave3dInstalledDisplayDriversSize; - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -65,6 +67,7 @@ void describe('projector/windows', () => { const p = new ProjectorWindows(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.crlf.bin'); p.includeXtras = { Scripting: null, // eslint-disable-next-line @typescript-eslint/naming-convention @@ -72,7 +75,7 @@ void describe('projector/windows', () => { // eslint-disable-next-line @typescript-eslint/naming-convention 'GIF Agent.x32': null }; - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -86,6 +89,7 @@ void describe('projector/windows', () => { const p = new ProjectorWindows(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.crlf.bin'); p.includeXtras = { Scripting: 'Scripting RENAMED', // eslint-disable-next-line @typescript-eslint/naming-convention @@ -93,7 +97,7 @@ void describe('projector/windows', () => { // eslint-disable-next-line @typescript-eslint/naming-convention 'GIF Agent.x32': 'GIF Agent RENAMED.x32' }; - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -107,8 +111,9 @@ void describe('projector/windows', () => { const p = new ProjectorWindows(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.crlf.bin'); p.shockwave = true; - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -122,6 +127,7 @@ void describe('projector/windows', () => { const p = new ProjectorWindows(dest); p.skeleton = await getSkeleton(); + p.configFile = fixtureFile('config.ini.crlf.bin'); p.nestXtrasConfiguration = true; p.includeXtras = { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -129,7 +135,7 @@ void describe('projector/windows', () => { }; p.patchShockwave3dInstalledDisplayDriversSize = patchShockwave3dInstalledDisplayDriversSize; - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'), @@ -143,6 +149,7 @@ void describe('projector/windows', () => { const p = new ProjectorWindows(dest); p.skeleton = await getSkeleton(); + p.configData = fixtureFile('config.ini.crlf.bin'); p.lingoFile = fixtureFile('lingo.ini.crlf.bin'); p.splashImageFile = fixtureFile('splash.bmp'); p.nestXtrasConfiguration = true; @@ -154,7 +161,7 @@ void describe('projector/windows', () => { patchShockwave3dInstalledDisplayDriversSize; p.iconFile = fixtureFile('icon.ico'); p.versionStrings = versionStrings; - await p.withFile(fixtureFile('config.ini.crlf.bin')); + await p.write(); await copyFile( fixtureFile('dir7.dir'),