@table-js/platform 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/builder.ts","../src/adapters/tizen.ts","../src/adapters/webos.ts","../src/adapters/android.ts"],"sourcesContent":["export { build } from './builder'\nexport type { Platform, BuildOptions, PlatformAdapter, ManifestBase } from './types'\nexport { TizenAdapter } from './adapters/tizen'\nexport { WebOSAdapter } from './adapters/webos'\nexport { AndroidAdapter } from './adapters/android'\n","import { build as viteBuild } from 'vite'\nimport type { BuildOptions, Platform, PlatformAdapter } from './types'\nimport { TizenAdapter } from './adapters/tizen'\nimport { WebOSAdapter } from './adapters/webos'\nimport { AndroidAdapter } from './adapters/android'\n\nexport async function build(platform: Platform, options: Partial<BuildOptions> = {}) {\n const opts: BuildOptions = {\n platform,\n outDir: options.outDir || 'dist',\n minify: options.minify ?? true,\n sourcemap: options.sourcemap ?? false,\n }\n\n if (platform === 'all') {\n await Promise.all([\n buildPlatform('tizen', opts),\n buildPlatform('webos', opts),\n buildPlatform('android', opts),\n ])\n return\n }\n\n await buildPlatform(platform, opts)\n}\n\nasync function buildPlatform(platform: Platform, options: BuildOptions) {\n const adapter = getAdapter(platform)\n\n await viteBuild({\n build: {\n outDir: `${options.outDir}/${platform}`,\n minify: options.minify,\n sourcemap: options.sourcemap,\n emptyOutDir: true,\n },\n define: {\n __TABLE_PLATFORM__: JSON.stringify(platform),\n },\n })\n\n await adapter.build(options)\n\n const packagePath = await adapter.package(`${options.outDir}/${platform}`)\n console.log(`✓ ${adapter.name} package created: ${packagePath}`)\n}\n\nfunction getAdapter(platform: Platform): PlatformAdapter {\n switch (platform) {\n case 'tizen':\n return new TizenAdapter()\n case 'webos':\n return new WebOSAdapter()\n case 'android':\n return new AndroidAdapter()\n default:\n throw new Error(`Unknown platform: ${platform}`)\n }\n}\n","import path from 'path'\nimport fs from 'fs-extra'\nimport archiver from 'archiver'\nimport { createWriteStream } from 'fs'\nimport type { BuildOptions, PlatformAdapter, ManifestBase } from '../types'\n\nexport class TizenAdapter implements PlatformAdapter {\n name = 'Tizen (Samsung)'\n\n async build(options: BuildOptions): Promise<void> {\n const distDir = `${options.outDir}/tizen`\n await this.createConfig(distDir)\n }\n\n async package(distDir: string): Promise<string> {\n const outputPath = path.join(distDir, '..', 'app.wgt')\n\n await new Promise<void>((resolve, reject) => {\n const output = createWriteStream(outputPath)\n const archive = archiver('zip', { zlib: { level: 9 } })\n\n output.on('close', () => resolve())\n archive.on('error', reject)\n\n archive.pipe(output)\n archive.directory(distDir, false)\n archive.finalize()\n })\n\n return outputPath\n }\n\n private async createConfig(distDir: string) {\n const pkg = await this.readPackageJson()\n\n const config = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<widget xmlns=\"http://www.w3.org/ns/widgets\" \n xmlns:tizen=\"http://tizen.org/ns/widgets\"\n id=\"${pkg.id || 'com.example.app'}\"\n version=\"${pkg.version}\"\n viewmodes=\"maximized\">\n <tizen:application id=\"${pkg.id || 'com.example.app'}.App\" \n package=\"${pkg.id || 'com.example.app'}\"\n required_version=\"6.0\"/>\n <content src=\"index.html\"/>\n <feature name=\"http://tizen.org/feature/screen.size.all\"/>\n <icon src=\"icon.png\"/>\n <name>${pkg.name}</name>\n <tizen:profile name=\"tv\"/>\n <tizen:setting screen-orientation=\"landscape\" \n context-menu=\"disable\" \n background-support=\"disable\" \n encryption=\"disable\" \n install-location=\"auto\"/>\n</widget>`\n\n await fs.writeFile(path.join(distDir, 'config.xml'), config)\n }\n\n private async readPackageJson(): Promise<ManifestBase> {\n try {\n const pkg = await fs.readJson('package.json')\n return {\n id: pkg.tizenAppId || pkg.name,\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n }\n } catch {\n return {\n id: 'com.example.app',\n name: 'Table App',\n version: '1.0.0',\n }\n }\n }\n}\n","import path from 'path'\nimport fs from 'fs-extra'\nimport archiver from 'archiver'\nimport { createWriteStream } from 'fs'\nimport type { BuildOptions, PlatformAdapter, ManifestBase } from '../types'\n\nexport class WebOSAdapter implements PlatformAdapter {\n name = 'webOS (LG)'\n\n async build(options: BuildOptions): Promise<void> {\n const distDir = `${options.outDir}/webos`\n await this.createAppInfo(distDir)\n }\n\n async package(distDir: string): Promise<string> {\n const outputPath = path.join(distDir, '..', 'app.ipk')\n\n await new Promise<void>((resolve, reject) => {\n const output = createWriteStream(outputPath)\n const archive = archiver('tar', { gzip: true })\n\n output.on('close', () => resolve())\n archive.on('error', reject)\n\n archive.pipe(output)\n archive.directory(distDir, false)\n archive.finalize()\n })\n\n return outputPath\n }\n\n private async createAppInfo(distDir: string) {\n const pkg = await this.readPackageJson()\n\n const appInfo = {\n id: pkg.id || 'com.example.app',\n version: pkg.version,\n vendor: 'Table.js',\n type: 'web',\n main: 'index.html',\n title: pkg.name,\n icon: 'icon.png',\n largeIcon: 'icon.png',\n bgImage: 'splash.png',\n resolution: '1920x1080',\n disallowScrollingInMainWindow: true,\n }\n\n await fs.writeJson(path.join(distDir, 'appinfo.json'), appInfo, { spaces: 2 })\n }\n\n private async readPackageJson(): Promise<ManifestBase> {\n try {\n const pkg = await fs.readJson('package.json')\n return {\n id: pkg.webosAppId || pkg.name,\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n }\n } catch {\n return {\n id: 'com.example.app',\n name: 'Table App',\n version: '1.0.0',\n }\n }\n }\n}\n","import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport type { BuildOptions, PlatformAdapter, ManifestBase } from '../types'\n\nexport class AndroidAdapter implements PlatformAdapter {\n name = 'Android TV'\n\n async build(options: BuildOptions): Promise<void> {\n const distDir = `${options.outDir}/android`\n await this.createWebViewProject(distDir)\n }\n\n async package(distDir: string): Promise<string> {\n const projectDir = path.join(distDir, 'android-project')\n\n try {\n await execa('./gradlew', ['assembleRelease'], {\n cwd: projectDir,\n stdio: 'inherit',\n })\n\n const apkPath = path.join(projectDir, 'app/build/outputs/apk/release/app-release.apk')\n\n const outputPath = path.join(distDir, '..', 'app.apk')\n await fs.copy(apkPath, outputPath)\n\n return outputPath\n } catch (error) {\n throw new Error('Android build failed. Make sure Android SDK is installed.')\n }\n }\n\n private async createWebViewProject(distDir: string) {\n const pkg = await this.readPackageJson()\n const projectDir = path.join(distDir, 'android-project')\n\n await fs.ensureDir(projectDir)\n await fs.copy(distDir, path.join(projectDir, 'app/src/main/assets/www'))\n\n await this.createManifest(projectDir, pkg)\n await this.createGradleFiles(projectDir, pkg)\n await this.createMainActivity(projectDir, pkg)\n }\n\n private async createManifest(projectDir: string, pkg: ManifestBase) {\n const manifest = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"${pkg.id || 'com.example.app'}\">\n\n <uses-permission android:name=\"android.permission.INTERNET\" />\n <uses-feature android:name=\"android.software.leanback\" android:required=\"true\" />\n <uses-feature android:name=\"android.hardware.touchscreen\" android:required=\"false\" />\n\n <application\n android:allowBackup=\"true\"\n android:icon=\"@mipmap/ic_launcher\"\n android:label=\"${pkg.name}\"\n android:banner=\"@drawable/banner\"\n android:theme=\"@style/Theme.Leanback\">\n \n <activity\n android:name=\".MainActivity\"\n android:exported=\"true\"\n android:screenOrientation=\"landscape\">\n <intent-filter>\n <action android:name=\"android.intent.action.MAIN\" />\n <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n </intent-filter>\n </activity>\n </application>\n</manifest>`\n\n const manifestPath = path.join(projectDir, 'app/src/main/AndroidManifest.xml')\n await fs.ensureDir(path.dirname(manifestPath))\n await fs.writeFile(manifestPath, manifest)\n }\n\n private async createGradleFiles(projectDir: string, pkg: ManifestBase) {\n const buildGradle = `plugins {\n id 'com.android.application'\n}\n\nandroid {\n namespace '${pkg.id || 'com.example.app'}'\n compileSdk 34\n\n defaultConfig {\n applicationId \"${pkg.id || 'com.example.app'}\"\n minSdk 21\n targetSdk 34\n versionCode 1\n versionName \"${pkg.version}\"\n }\n\n buildTypes {\n release {\n minifyEnabled true\n proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')\n }\n }\n}\n\ndependencies {\n implementation 'androidx.leanback:leanback:1.0.0'\n}`\n\n await fs.writeFile(path.join(projectDir, 'app/build.gradle'), buildGradle)\n }\n\n private async createMainActivity(projectDir: string, pkg: ManifestBase) {\n const packageName = pkg.id || 'com.example.app'\n const activity = `package ${packageName};\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.webkit.WebView;\nimport android.webkit.WebSettings;\n\npublic class MainActivity extends Activity {\n private WebView webView;\n\n @Override\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n \n webView = new WebView(this);\n setContentView(webView);\n \n WebSettings settings = webView.getSettings();\n settings.setJavaScriptEnabled(true);\n settings.setDomStorageEnabled(true);\n settings.setAllowFileAccess(true);\n \n webView.loadUrl(\"file:///android_asset/www/index.html\");\n }\n}`\n\n const activityPath = path.join(\n projectDir,\n `app/src/main/java/${packageName.replace(/\\./g, '/')}/MainActivity.java`,\n )\n await fs.ensureDir(path.dirname(activityPath))\n await fs.writeFile(activityPath, activity)\n }\n\n private async readPackageJson(): Promise<ManifestBase> {\n try {\n const pkg = await fs.readJson('package.json')\n return {\n id: pkg.androidAppId || pkg.name.replace(/[^a-z0-9]/gi, '.').toLowerCase(),\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n }\n } catch {\n return {\n id: 'com.example.app',\n name: 'Table App',\n version: '1.0.0',\n }\n }\n }\n}\n"],"mappings":"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","names":["path","archiver","fs","import_path","import_fs_extra","import_archiver","import_fs","path","archiver","fs","import_path","import_fs_extra","path","fs","viteBuild"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/builder.ts","../src/adapters/tizen.ts","../src/adapters/webos.ts","../src/adapters/android.ts"],"sourcesContent":["export { build } from './builder'\nexport type { Platform, Target, Mode, BuildOptions, PlatformAdapter, ManifestBase } from './types'\nexport { TizenAdapter } from './adapters/tizen'\nexport { WebOSAdapter } from './adapters/webos'\nexport { AndroidAdapter } from './adapters/android'\n","import fs from 'fs-extra'\nimport { build as viteBuild } from 'vite'\nimport type { BuildOptions, Mode, Platform, PlatformAdapter, Target } from './types'\nimport { TizenAdapter } from './adapters/tizen'\nimport { WebOSAdapter } from './adapters/webos'\nimport { AndroidAdapter } from './adapters/android'\n\nexport async function build(platform: Platform, options: Partial<BuildOptions> = {}) {\n const opts = {\n outDir: options.outDir || 'dist',\n mode: options.mode || 'webview',\n minify: options.minify ?? true,\n sourcemap: options.sourcemap ?? false,\n }\n\n if (platform === 'all') {\n await Promise.all([\n buildPlatform('tizen', opts),\n buildPlatform('webos', opts),\n buildPlatform('android', opts),\n ])\n return\n }\n\n await buildPlatform(platform, opts)\n}\n\nasync function buildPlatform(platform: Target, base: Omit<BuildOptions, 'dir' | 'platform'>) {\n const options: BuildOptions = {\n ...base,\n dir: out(base.outDir, platform, base.mode),\n platform,\n }\n const adapter = getAdapter(platform)\n\n if (web(platform, options.mode)) {\n await viteBuild({\n build: {\n outDir: dir(options),\n minify: options.minify,\n sourcemap: options.sourcemap,\n emptyOutDir: true,\n },\n define: {\n __TABLE_PLATFORM__: JSON.stringify(platform),\n },\n })\n } else {\n await fs.emptyDir(options.dir)\n }\n\n await adapter.build(options)\n\n const packagePath = await adapter.package(options)\n console.log(`✓ ${adapter.name} package created: ${packagePath}`)\n}\n\nfunction getAdapter(platform: Target): PlatformAdapter {\n switch (platform) {\n case 'tizen':\n return new TizenAdapter()\n case 'webos':\n return new WebOSAdapter()\n case 'android':\n return new AndroidAdapter()\n default:\n throw new Error(`Unknown platform: ${platform}`)\n }\n}\n\nfunction out(root: string, platform: Target, mode: Mode) {\n if (platform === 'android' && mode === 'native') {\n return `${root}/android-native`\n }\n\n return `${root}/${platform}`\n}\n\nfunction dir(options: BuildOptions) {\n if (options.platform === 'android') {\n return `${options.dir}/www`\n }\n\n return options.dir\n}\n\nfunction web(platform: Target, mode: Mode) {\n return platform !== 'android' || mode === 'webview'\n}\n","import path from 'path'\nimport fs from 'fs-extra'\nimport archiver from 'archiver'\nimport { createWriteStream } from 'fs'\nimport type { BuildOptions, PlatformAdapter, ManifestBase } from '../types'\n\nexport class TizenAdapter implements PlatformAdapter {\n name = 'Tizen (Samsung)'\n\n async build(options: BuildOptions): Promise<void> {\n await this.createConfig(options.dir)\n }\n\n async package(options: BuildOptions): Promise<string> {\n const outputPath = path.join(options.dir, '..', 'app.wgt')\n\n await new Promise<void>((resolve, reject) => {\n const output = createWriteStream(outputPath)\n const archive = archiver('zip', { zlib: { level: 9 } })\n\n output.on('close', () => resolve())\n archive.on('error', reject)\n\n archive.pipe(output)\n archive.directory(options.dir, false)\n archive.finalize()\n })\n\n return outputPath\n }\n\n private async createConfig(distDir: string) {\n const pkg = await this.readPackageJson()\n\n const config = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<widget xmlns=\"http://www.w3.org/ns/widgets\" \n xmlns:tizen=\"http://tizen.org/ns/widgets\"\n id=\"${pkg.id || 'com.example.app'}\"\n version=\"${pkg.version}\"\n viewmodes=\"maximized\">\n <tizen:application id=\"${pkg.id || 'com.example.app'}.App\" \n package=\"${pkg.id || 'com.example.app'}\"\n required_version=\"6.0\"/>\n <content src=\"index.html\"/>\n <feature name=\"http://tizen.org/feature/screen.size.all\"/>\n <icon src=\"icon.png\"/>\n <name>${pkg.name}</name>\n <tizen:profile name=\"tv\"/>\n <tizen:setting screen-orientation=\"landscape\" \n context-menu=\"disable\" \n background-support=\"disable\" \n encryption=\"disable\" \n install-location=\"auto\"/>\n</widget>`\n\n await fs.writeFile(path.join(distDir, 'config.xml'), config)\n }\n\n private async readPackageJson(): Promise<ManifestBase> {\n try {\n const pkg = await fs.readJson('package.json')\n return {\n id: pkg.tizenAppId || pkg.name,\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n }\n } catch {\n return {\n id: 'com.example.app',\n name: 'Table App',\n version: '1.0.0',\n }\n }\n }\n}\n","import path from 'path'\nimport fs from 'fs-extra'\nimport archiver from 'archiver'\nimport { createWriteStream } from 'fs'\nimport type { BuildOptions, PlatformAdapter, ManifestBase } from '../types'\n\nexport class WebOSAdapter implements PlatformAdapter {\n name = 'webOS (LG)'\n\n async build(options: BuildOptions): Promise<void> {\n await this.createAppInfo(options.dir)\n }\n\n async package(options: BuildOptions): Promise<string> {\n const outputPath = path.join(options.dir, '..', 'app.ipk')\n\n await new Promise<void>((resolve, reject) => {\n const output = createWriteStream(outputPath)\n const archive = archiver('tar', { gzip: true })\n\n output.on('close', () => resolve())\n archive.on('error', reject)\n\n archive.pipe(output)\n archive.directory(options.dir, false)\n archive.finalize()\n })\n\n return outputPath\n }\n\n private async createAppInfo(distDir: string) {\n const pkg = await this.readPackageJson()\n\n const appInfo = {\n id: pkg.id || 'com.example.app',\n version: pkg.version,\n vendor: 'Table.js',\n type: 'web',\n main: 'index.html',\n title: pkg.name,\n icon: 'icon.png',\n largeIcon: 'icon.png',\n bgImage: 'splash.png',\n resolution: '1920x1080',\n disallowScrollingInMainWindow: true,\n }\n\n await fs.writeJson(path.join(distDir, 'appinfo.json'), appInfo, { spaces: 2 })\n }\n\n private async readPackageJson(): Promise<ManifestBase> {\n try {\n const pkg = await fs.readJson('package.json')\n return {\n id: pkg.webosAppId || pkg.name,\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n }\n } catch {\n return {\n id: 'com.example.app',\n name: 'Table App',\n version: '1.0.0',\n }\n }\n }\n}\n","import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport type { BuildOptions, PlatformAdapter, ManifestBase } from '../types'\n\nexport class AndroidAdapter implements PlatformAdapter {\n name = 'Android TV'\n\n async build(options: BuildOptions): Promise<void> {\n if (options.mode === 'native') {\n await this.native(options.dir)\n return\n }\n\n await this.web(options.dir)\n }\n\n async package(options: BuildOptions): Promise<string> {\n const proj = path.join(options.dir, 'project')\n const cmd = await this.cmd(proj)\n\n if (!cmd) {\n return proj\n }\n\n try {\n await execa(cmd, ['assembleRelease'], {\n cwd: proj,\n stdio: 'inherit',\n })\n\n const apk = path.join(proj, 'app/build/outputs/apk/release/app-release.apk')\n const out = path.join(options.dir, '..', this.apk(options))\n await fs.copy(apk, out)\n\n return out\n } catch {\n throw new Error('Android build failed. Install the Android SDK and Gradle, or add a Gradle wrapper.')\n }\n }\n\n private async web(dir: string) {\n const pkg = await this.pkg()\n const proj = path.join(dir, 'project')\n\n await fs.remove(proj)\n await this.root(proj, pkg)\n await this.man(proj, pkg, true)\n await this.app(proj, pkg, false)\n await this.res(proj)\n await this.webKt(proj, pkg)\n await fs.copy(path.join(dir, 'www'), path.join(proj, 'app/src/main/assets/www'))\n }\n\n private async native(dir: string) {\n const pkg = await this.pkg()\n const proj = path.join(dir, 'project')\n\n await fs.remove(proj)\n await this.root(proj, pkg)\n await this.man(proj, pkg, false)\n await this.app(proj, pkg, true)\n await this.res(proj)\n await this.nativeKt(proj, pkg)\n }\n\n private async root(proj: string, pkg: ManifestBase) {\n const settings = `pluginManagement {\n repositories {\n google()\n mavenCentral()\n gradlePluginPortal()\n }\n}\n\ndependencyResolutionManagement {\n repositoriesMode.set(org.gradle.api.initialization.resolve.RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n repositories {\n google()\n mavenCentral()\n }\n}\n\nrootProject.name = \"${this.safe(pkg.name)}\"\ninclude(\":app\")\n`\n\n const build = `plugins {\n id(\"com.android.application\") version \"9.1.0\" apply false\n id(\"org.jetbrains.kotlin.android\") version \"2.3.10\" apply false\n id(\"org.jetbrains.kotlin.plugin.compose\") version \"2.3.10\" apply false\n}\n`\n\n const props = `org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\nandroid.useAndroidX=true\nkotlin.code.style=official\n`\n\n await fs.ensureDir(proj)\n await fs.ensureDir(path.join(proj, 'app'))\n await fs.writeFile(path.join(proj, 'settings.gradle.kts'), settings)\n await fs.writeFile(path.join(proj, 'build.gradle.kts'), build)\n await fs.writeFile(path.join(proj, 'gradle.properties'), props)\n await fs.writeFile(path.join(proj, 'app/proguard-rules.pro'), '')\n }\n\n private async man(proj: string, pkg: ManifestBase, web: boolean) {\n const net = web ? '<uses-permission android:name=\"android.permission.INTERNET\" />\\n' : ''\n const clear = web ? ' android:usesCleartextTraffic=\"true\"\\n' : ''\n const manifest = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"${pkg.id || 'com.example.app'}\">\n\n ${net} <uses-feature android:name=\"android.software.leanback\" android:required=\"true\" />\n <uses-feature android:name=\"android.hardware.touchscreen\" android:required=\"false\" />\n\n <application\n android:allowBackup=\"true\"\n android:banner=\"@drawable/banner\"\n android:icon=\"@android:drawable/sym_def_app_icon\"\n android:label=\"${pkg.name}\"\n${clear} android:theme=\"@android:style/Theme.DeviceDefault.NoActionBar\">\n\n <activity\n android:name=\".MainActivity\"\n android:exported=\"true\"\n android:screenOrientation=\"landscape\">\n <intent-filter>\n <action android:name=\"android.intent.action.MAIN\" />\n <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n </intent-filter>\n </activity>\n </application>\n</manifest>`\n\n const file = path.join(proj, 'app/src/main/AndroidManifest.xml')\n await fs.ensureDir(path.dirname(file))\n await fs.writeFile(file, manifest)\n }\n\n private async app(proj: string, pkg: ManifestBase, native: boolean) {\n const plugins = native\n ? `plugins {\n id(\"com.android.application\")\n id(\"org.jetbrains.kotlin.android\")\n id(\"org.jetbrains.kotlin.plugin.compose\")\n}`\n : `plugins {\n id(\"com.android.application\")\n id(\"org.jetbrains.kotlin.android\")\n}`\n\n const flags = native\n ? `\n buildFeatures {\n compose = true\n }`\n : ''\n\n const deps = native\n ? `dependencies {\n val bom = platform(\"androidx.compose:compose-bom:2026.02.01\")\n\n implementation(bom)\n androidTestImplementation(bom)\n\n implementation(\"androidx.activity:activity-compose:1.12.4\")\n implementation(\"androidx.compose.foundation:foundation\")\n implementation(\"androidx.compose.ui:ui\")\n implementation(\"androidx.compose.ui:ui-tooling-preview\")\n implementation(\"androidx.tv:tv-material:1.0.0\")\n debugImplementation(\"androidx.compose.ui:ui-tooling\")\n}`\n : `dependencies {\n implementation(\"androidx.leanback:leanback:1.0.0\")\n}`\n\n const build = `${plugins}\nandroid {\n namespace = \"${pkg.id || 'com.example.app'}\"\n compileSdk = 34\n\n defaultConfig {\n applicationId = \"${pkg.id || 'com.example.app'}\"\n minSdk = 21\n targetSdk = 34\n versionCode = 1\n versionName = \"${pkg.version}\"\n }\n\n compileOptions {\n sourceCompatibility = JavaVersion.VERSION_17\n targetCompatibility = JavaVersion.VERSION_17\n }\n\n kotlinOptions {\n jvmTarget = \"17\"\n }\n${flags}\n\n buildTypes {\n release {\n isMinifyEnabled = true\n proguardFiles(\n getDefaultProguardFile(\"proguard-android-optimize.txt\"),\n \"proguard-rules.pro\",\n )\n }\n }\n}\n\n${deps}\n`\n\n await fs.writeFile(path.join(proj, 'app/build.gradle.kts'), build)\n }\n\n private async res(proj: string) {\n const banner = `<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\"rectangle\">\n <solid android:color=\"#10151E\" />\n <corners android:radius=\"18dp\" />\n</shape>\n`\n\n const file = path.join(proj, 'app/src/main/res/drawable/banner.xml')\n await fs.ensureDir(path.dirname(file))\n await fs.writeFile(file, banner)\n }\n\n private async webKt(proj: string, pkg: ManifestBase) {\n const id = pkg.id || 'com.example.app'\n const main = `package ${id}\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.webkit.WebSettings\nimport android.webkit.WebView\n\nclass MainActivity : Activity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n\n val web = WebView(this)\n setContentView(web)\n\n val set: WebSettings = web.settings\n set.javaScriptEnabled = true\n set.domStorageEnabled = true\n set.allowFileAccess = true\n\n web.loadUrl(\"file:///android_asset/www/index.html\")\n }\n}\n`\n\n const file = path.join(proj, `app/src/main/java/${id.replace(/\\./g, '/')}/MainActivity.kt`)\n await fs.ensureDir(path.dirname(file))\n await fs.writeFile(file, main)\n }\n\n private async nativeKt(proj: string, pkg: ManifestBase) {\n const id = pkg.id || 'com.example.app'\n const main = `package ${id}\n\nimport android.os.Bundle\nimport androidx.activity.ComponentActivity\nimport androidx.activity.compose.setContent\nimport androidx.compose.foundation.background\nimport androidx.compose.foundation.layout.Arrangement\nimport androidx.compose.foundation.layout.Box\nimport androidx.compose.foundation.layout.Column\nimport androidx.compose.foundation.layout.fillMaxSize\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.foundation.shape.RoundedCornerShape\nimport androidx.compose.ui.Alignment\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.graphics.Color\nimport androidx.compose.ui.text.style.TextAlign\nimport androidx.compose.ui.unit.dp\nimport androidx.tv.material3.Button\nimport androidx.tv.material3.MaterialTheme\nimport androidx.tv.material3.Surface\nimport androidx.tv.material3.Text\n\nclass MainActivity : ComponentActivity() {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(savedInstanceState)\n setContent {\n MaterialTheme {\n Surface(modifier = Modifier.fillMaxSize()) {\n Box(\n modifier = Modifier\n .fillMaxSize()\n .background(Color(0xFF05070A))\n .padding(48.dp),\n contentAlignment = Alignment.Center,\n ) {\n Column(\n modifier = Modifier\n .fillMaxWidth()\n .background(Color(0xFF10151E), RoundedCornerShape(28.dp))\n .padding(32.dp),\n horizontalAlignment = Alignment.CenterHorizontally,\n verticalArrangement = Arrangement.spacedBy(20.dp),\n ) {\n Text(\n text = \"${pkg.name}\",\n style = MaterialTheme.typography.displayMedium,\n textAlign = TextAlign.Center,\n )\n Text(\n text = \"Android native mode uses Kotlin and Compose for TV instead of WebView.\",\n style = MaterialTheme.typography.bodyLarge,\n textAlign = TextAlign.Center,\n color = Color(0xFFD4D7DD),\n )\n Button(onClick = {}) {\n Text(\"Ready\")\n }\n }\n }\n }\n }\n }\n }\n}\n`\n\n const file = path.join(proj, `app/src/main/java/${id.replace(/\\./g, '/')}/MainActivity.kt`)\n await fs.ensureDir(path.dirname(file))\n await fs.writeFile(file, main)\n }\n\n private async cmd(proj: string) {\n if (await fs.pathExists(path.join(proj, 'gradlew'))) {\n return './gradlew'\n }\n\n if (await this.has('gradle')) {\n return 'gradle'\n }\n\n return null\n }\n\n private async has(cmd: string) {\n try {\n await execa(cmd, ['--version'])\n return true\n } catch {\n return false\n }\n }\n\n private apk(options: BuildOptions) {\n return options.mode === 'native' ? 'app-native.apk' : 'app.apk'\n }\n\n private safe(name: string) {\n return name.replace(/[^a-z0-9._-]/gi, '-')\n }\n\n private async pkg(): Promise<ManifestBase> {\n try {\n const pkg = await fs.readJson('package.json')\n return {\n id: pkg.androidAppId || this.id(pkg.name),\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n }\n } catch {\n return {\n id: 'com.example.app',\n name: 'Table App',\n version: '1.0.0',\n }\n }\n }\n\n private id(name: string) {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '.')\n .replace(/^\\.+|\\.+$/g, '')\n .replace(/\\.\\.+/g, '.')\n\n return `com.tablejs.${slug || 'app'}`\n }\n}\n"],"mappings":"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","names":["import_fs_extra","path","archiver","fs","import_path","import_fs_extra","import_archiver","import_fs","path","archiver","fs","import_path","import_fs_extra","path","out","fs","dir","build","web","viteBuild","fs"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/builder.ts
2
+ import fs4 from "fs-extra";
2
3
  import { build as viteBuild } from "vite";
3
4
 
4
5
  // src/adapters/tizen.ts
@@ -9,18 +10,17 @@ import { createWriteStream } from "fs";
9
10
  var TizenAdapter = class {
10
11
  name = "Tizen (Samsung)";
11
12
  async build(options) {
12
- const distDir = `${options.outDir}/tizen`;
13
- await this.createConfig(distDir);
13
+ await this.createConfig(options.dir);
14
14
  }
15
- async package(distDir) {
16
- const outputPath = path.join(distDir, "..", "app.wgt");
15
+ async package(options) {
16
+ const outputPath = path.join(options.dir, "..", "app.wgt");
17
17
  await new Promise((resolve, reject) => {
18
18
  const output = createWriteStream(outputPath);
19
19
  const archive = archiver("zip", { zlib: { level: 9 } });
20
20
  output.on("close", () => resolve());
21
21
  archive.on("error", reject);
22
22
  archive.pipe(output);
23
- archive.directory(distDir, false);
23
+ archive.directory(options.dir, false);
24
24
  archive.finalize();
25
25
  });
26
26
  return outputPath;
@@ -76,18 +76,17 @@ import { createWriteStream as createWriteStream2 } from "fs";
76
76
  var WebOSAdapter = class {
77
77
  name = "webOS (LG)";
78
78
  async build(options) {
79
- const distDir = `${options.outDir}/webos`;
80
- await this.createAppInfo(distDir);
79
+ await this.createAppInfo(options.dir);
81
80
  }
82
- async package(distDir) {
83
- const outputPath = path2.join(distDir, "..", "app.ipk");
81
+ async package(options) {
82
+ const outputPath = path2.join(options.dir, "..", "app.ipk");
84
83
  await new Promise((resolve, reject) => {
85
84
  const output = createWriteStream2(outputPath);
86
85
  const archive = archiver2("tar", { gzip: true });
87
86
  output.on("close", () => resolve());
88
87
  archive.on("error", reject);
89
88
  archive.pipe(output);
90
- archive.directory(distDir, false);
89
+ archive.directory(options.dir, false);
91
90
  archive.finalize();
92
91
  });
93
92
  return outputPath;
@@ -135,49 +134,106 @@ import { execa } from "execa";
135
134
  var AndroidAdapter = class {
136
135
  name = "Android TV";
137
136
  async build(options) {
138
- const distDir = `${options.outDir}/android`;
139
- await this.createWebViewProject(distDir);
137
+ if (options.mode === "native") {
138
+ await this.native(options.dir);
139
+ return;
140
+ }
141
+ await this.web(options.dir);
140
142
  }
141
- async package(distDir) {
142
- const projectDir = path3.join(distDir, "android-project");
143
+ async package(options) {
144
+ const proj = path3.join(options.dir, "project");
145
+ const cmd = await this.cmd(proj);
146
+ if (!cmd) {
147
+ return proj;
148
+ }
143
149
  try {
144
- await execa("./gradlew", ["assembleRelease"], {
145
- cwd: projectDir,
150
+ await execa(cmd, ["assembleRelease"], {
151
+ cwd: proj,
146
152
  stdio: "inherit"
147
153
  });
148
- const apkPath = path3.join(projectDir, "app/build/outputs/apk/release/app-release.apk");
149
- const outputPath = path3.join(distDir, "..", "app.apk");
150
- await fs3.copy(apkPath, outputPath);
151
- return outputPath;
152
- } catch (error) {
153
- throw new Error("Android build failed. Make sure Android SDK is installed.");
154
+ const apk = path3.join(proj, "app/build/outputs/apk/release/app-release.apk");
155
+ const out2 = path3.join(options.dir, "..", this.apk(options));
156
+ await fs3.copy(apk, out2);
157
+ return out2;
158
+ } catch {
159
+ throw new Error("Android build failed. Install the Android SDK and Gradle, or add a Gradle wrapper.");
154
160
  }
155
161
  }
156
- async createWebViewProject(distDir) {
157
- const pkg = await this.readPackageJson();
158
- const projectDir = path3.join(distDir, "android-project");
159
- await fs3.ensureDir(projectDir);
160
- await fs3.copy(distDir, path3.join(projectDir, "app/src/main/assets/www"));
161
- await this.createManifest(projectDir, pkg);
162
- await this.createGradleFiles(projectDir, pkg);
163
- await this.createMainActivity(projectDir, pkg);
162
+ async web(dir2) {
163
+ const pkg = await this.pkg();
164
+ const proj = path3.join(dir2, "project");
165
+ await fs3.remove(proj);
166
+ await this.root(proj, pkg);
167
+ await this.man(proj, pkg, true);
168
+ await this.app(proj, pkg, false);
169
+ await this.res(proj);
170
+ await this.webKt(proj, pkg);
171
+ await fs3.copy(path3.join(dir2, "www"), path3.join(proj, "app/src/main/assets/www"));
172
+ }
173
+ async native(dir2) {
174
+ const pkg = await this.pkg();
175
+ const proj = path3.join(dir2, "project");
176
+ await fs3.remove(proj);
177
+ await this.root(proj, pkg);
178
+ await this.man(proj, pkg, false);
179
+ await this.app(proj, pkg, true);
180
+ await this.res(proj);
181
+ await this.nativeKt(proj, pkg);
182
+ }
183
+ async root(proj, pkg) {
184
+ const settings = `pluginManagement {
185
+ repositories {
186
+ google()
187
+ mavenCentral()
188
+ gradlePluginPortal()
189
+ }
190
+ }
191
+
192
+ dependencyResolutionManagement {
193
+ repositoriesMode.set(org.gradle.api.initialization.resolve.RepositoriesMode.FAIL_ON_PROJECT_REPOS)
194
+ repositories {
195
+ google()
196
+ mavenCentral()
197
+ }
198
+ }
199
+
200
+ rootProject.name = "${this.safe(pkg.name)}"
201
+ include(":app")
202
+ `;
203
+ const build2 = `plugins {
204
+ id("com.android.application") version "9.1.0" apply false
205
+ id("org.jetbrains.kotlin.android") version "2.3.10" apply false
206
+ id("org.jetbrains.kotlin.plugin.compose") version "2.3.10" apply false
207
+ }
208
+ `;
209
+ const props = `org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
210
+ android.useAndroidX=true
211
+ kotlin.code.style=official
212
+ `;
213
+ await fs3.ensureDir(proj);
214
+ await fs3.ensureDir(path3.join(proj, "app"));
215
+ await fs3.writeFile(path3.join(proj, "settings.gradle.kts"), settings);
216
+ await fs3.writeFile(path3.join(proj, "build.gradle.kts"), build2);
217
+ await fs3.writeFile(path3.join(proj, "gradle.properties"), props);
218
+ await fs3.writeFile(path3.join(proj, "app/proguard-rules.pro"), "");
164
219
  }
165
- async createManifest(projectDir, pkg) {
220
+ async man(proj, pkg, web2) {
221
+ const net = web2 ? '<uses-permission android:name="android.permission.INTERNET" />\n' : "";
222
+ const clear = web2 ? ' android:usesCleartextTraffic="true"\n' : "";
166
223
  const manifest = `<?xml version="1.0" encoding="utf-8"?>
167
224
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
168
225
  package="${pkg.id || "com.example.app"}">
169
226
 
170
- <uses-permission android:name="android.permission.INTERNET" />
171
- <uses-feature android:name="android.software.leanback" android:required="true" />
227
+ ${net} <uses-feature android:name="android.software.leanback" android:required="true" />
172
228
  <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
173
229
 
174
230
  <application
175
231
  android:allowBackup="true"
176
- android:icon="@mipmap/ic_launcher"
177
- android:label="${pkg.name}"
178
232
  android:banner="@drawable/banner"
179
- android:theme="@style/Theme.Leanback">
180
-
233
+ android:icon="@android:drawable/sym_def_app_icon"
234
+ android:label="${pkg.name}"
235
+ ${clear} android:theme="@android:style/Theme.DeviceDefault.NoActionBar">
236
+
181
237
  <activity
182
238
  android:name=".MainActivity"
183
239
  android:exported="true"
@@ -189,79 +245,215 @@ var AndroidAdapter = class {
189
245
  </activity>
190
246
  </application>
191
247
  </manifest>`;
192
- const manifestPath = path3.join(projectDir, "app/src/main/AndroidManifest.xml");
193
- await fs3.ensureDir(path3.dirname(manifestPath));
194
- await fs3.writeFile(manifestPath, manifest);
248
+ const file = path3.join(proj, "app/src/main/AndroidManifest.xml");
249
+ await fs3.ensureDir(path3.dirname(file));
250
+ await fs3.writeFile(file, manifest);
195
251
  }
196
- async createGradleFiles(projectDir, pkg) {
197
- const buildGradle = `plugins {
198
- id 'com.android.application'
199
- }
252
+ async app(proj, pkg, native) {
253
+ const plugins = native ? `plugins {
254
+ id("com.android.application")
255
+ id("org.jetbrains.kotlin.android")
256
+ id("org.jetbrains.kotlin.plugin.compose")
257
+ }` : `plugins {
258
+ id("com.android.application")
259
+ id("org.jetbrains.kotlin.android")
260
+ }`;
261
+ const flags = native ? `
262
+ buildFeatures {
263
+ compose = true
264
+ }` : "";
265
+ const deps = native ? `dependencies {
266
+ val bom = platform("androidx.compose:compose-bom:2026.02.01")
200
267
 
268
+ implementation(bom)
269
+ androidTestImplementation(bom)
270
+
271
+ implementation("androidx.activity:activity-compose:1.12.4")
272
+ implementation("androidx.compose.foundation:foundation")
273
+ implementation("androidx.compose.ui:ui")
274
+ implementation("androidx.compose.ui:ui-tooling-preview")
275
+ implementation("androidx.tv:tv-material:1.0.0")
276
+ debugImplementation("androidx.compose.ui:ui-tooling")
277
+ }` : `dependencies {
278
+ implementation("androidx.leanback:leanback:1.0.0")
279
+ }`;
280
+ const build2 = `${plugins}
201
281
  android {
202
- namespace '${pkg.id || "com.example.app"}'
203
- compileSdk 34
282
+ namespace = "${pkg.id || "com.example.app"}"
283
+ compileSdk = 34
204
284
 
205
285
  defaultConfig {
206
- applicationId "${pkg.id || "com.example.app"}"
207
- minSdk 21
208
- targetSdk 34
209
- versionCode 1
210
- versionName "${pkg.version}"
286
+ applicationId = "${pkg.id || "com.example.app"}"
287
+ minSdk = 21
288
+ targetSdk = 34
289
+ versionCode = 1
290
+ versionName = "${pkg.version}"
291
+ }
292
+
293
+ compileOptions {
294
+ sourceCompatibility = JavaVersion.VERSION_17
295
+ targetCompatibility = JavaVersion.VERSION_17
296
+ }
297
+
298
+ kotlinOptions {
299
+ jvmTarget = "17"
211
300
  }
301
+ ${flags}
212
302
 
213
303
  buildTypes {
214
304
  release {
215
- minifyEnabled true
216
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
305
+ isMinifyEnabled = true
306
+ proguardFiles(
307
+ getDefaultProguardFile("proguard-android-optimize.txt"),
308
+ "proguard-rules.pro",
309
+ )
217
310
  }
218
311
  }
219
312
  }
220
313
 
221
- dependencies {
222
- implementation 'androidx.leanback:leanback:1.0.0'
223
- }`;
224
- await fs3.writeFile(path3.join(projectDir, "app/build.gradle"), buildGradle);
314
+ ${deps}
315
+ `;
316
+ await fs3.writeFile(path3.join(proj, "app/build.gradle.kts"), build2);
225
317
  }
226
- async createMainActivity(projectDir, pkg) {
227
- const packageName = pkg.id || "com.example.app";
228
- const activity = `package ${packageName};
318
+ async res(proj) {
319
+ const banner = `<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
320
+ <solid android:color="#10151E" />
321
+ <corners android:radius="18dp" />
322
+ </shape>
323
+ `;
324
+ const file = path3.join(proj, "app/src/main/res/drawable/banner.xml");
325
+ await fs3.ensureDir(path3.dirname(file));
326
+ await fs3.writeFile(file, banner);
327
+ }
328
+ async webKt(proj, pkg) {
329
+ const id = pkg.id || "com.example.app";
330
+ const main = `package ${id}
331
+
332
+ import android.app.Activity
333
+ import android.os.Bundle
334
+ import android.webkit.WebSettings
335
+ import android.webkit.WebView
336
+
337
+ class MainActivity : Activity() {
338
+ override fun onCreate(savedInstanceState: Bundle?) {
339
+ super.onCreate(savedInstanceState)
229
340
 
230
- import android.app.Activity;
231
- import android.os.Bundle;
232
- import android.webkit.WebView;
233
- import android.webkit.WebSettings;
341
+ val web = WebView(this)
342
+ setContentView(web)
234
343
 
235
- public class MainActivity extends Activity {
236
- private WebView webView;
344
+ val set: WebSettings = web.settings
345
+ set.javaScriptEnabled = true
346
+ set.domStorageEnabled = true
347
+ set.allowFileAccess = true
237
348
 
238
- @Override
239
- protected void onCreate(Bundle savedInstanceState) {
240
- super.onCreate(savedInstanceState);
241
-
242
- webView = new WebView(this);
243
- setContentView(webView);
244
-
245
- WebSettings settings = webView.getSettings();
246
- settings.setJavaScriptEnabled(true);
247
- settings.setDomStorageEnabled(true);
248
- settings.setAllowFileAccess(true);
249
-
250
- webView.loadUrl("file:///android_asset/www/index.html");
349
+ web.loadUrl("file:///android_asset/www/index.html")
251
350
  }
252
- }`;
253
- const activityPath = path3.join(
254
- projectDir,
255
- `app/src/main/java/${packageName.replace(/\./g, "/")}/MainActivity.java`
256
- );
257
- await fs3.ensureDir(path3.dirname(activityPath));
258
- await fs3.writeFile(activityPath, activity);
351
+ }
352
+ `;
353
+ const file = path3.join(proj, `app/src/main/java/${id.replace(/\./g, "/")}/MainActivity.kt`);
354
+ await fs3.ensureDir(path3.dirname(file));
355
+ await fs3.writeFile(file, main);
259
356
  }
260
- async readPackageJson() {
357
+ async nativeKt(proj, pkg) {
358
+ const id = pkg.id || "com.example.app";
359
+ const main = `package ${id}
360
+
361
+ import android.os.Bundle
362
+ import androidx.activity.ComponentActivity
363
+ import androidx.activity.compose.setContent
364
+ import androidx.compose.foundation.background
365
+ import androidx.compose.foundation.layout.Arrangement
366
+ import androidx.compose.foundation.layout.Box
367
+ import androidx.compose.foundation.layout.Column
368
+ import androidx.compose.foundation.layout.fillMaxSize
369
+ import androidx.compose.foundation.layout.fillMaxWidth
370
+ import androidx.compose.foundation.layout.padding
371
+ import androidx.compose.foundation.shape.RoundedCornerShape
372
+ import androidx.compose.ui.Alignment
373
+ import androidx.compose.ui.Modifier
374
+ import androidx.compose.ui.graphics.Color
375
+ import androidx.compose.ui.text.style.TextAlign
376
+ import androidx.compose.ui.unit.dp
377
+ import androidx.tv.material3.Button
378
+ import androidx.tv.material3.MaterialTheme
379
+ import androidx.tv.material3.Surface
380
+ import androidx.tv.material3.Text
381
+
382
+ class MainActivity : ComponentActivity() {
383
+ override fun onCreate(savedInstanceState: Bundle?) {
384
+ super.onCreate(savedInstanceState)
385
+ setContent {
386
+ MaterialTheme {
387
+ Surface(modifier = Modifier.fillMaxSize()) {
388
+ Box(
389
+ modifier = Modifier
390
+ .fillMaxSize()
391
+ .background(Color(0xFF05070A))
392
+ .padding(48.dp),
393
+ contentAlignment = Alignment.Center,
394
+ ) {
395
+ Column(
396
+ modifier = Modifier
397
+ .fillMaxWidth()
398
+ .background(Color(0xFF10151E), RoundedCornerShape(28.dp))
399
+ .padding(32.dp),
400
+ horizontalAlignment = Alignment.CenterHorizontally,
401
+ verticalArrangement = Arrangement.spacedBy(20.dp),
402
+ ) {
403
+ Text(
404
+ text = "${pkg.name}",
405
+ style = MaterialTheme.typography.displayMedium,
406
+ textAlign = TextAlign.Center,
407
+ )
408
+ Text(
409
+ text = "Android native mode uses Kotlin and Compose for TV instead of WebView.",
410
+ style = MaterialTheme.typography.bodyLarge,
411
+ textAlign = TextAlign.Center,
412
+ color = Color(0xFFD4D7DD),
413
+ )
414
+ Button(onClick = {}) {
415
+ Text("Ready")
416
+ }
417
+ }
418
+ }
419
+ }
420
+ }
421
+ }
422
+ }
423
+ }
424
+ `;
425
+ const file = path3.join(proj, `app/src/main/java/${id.replace(/\./g, "/")}/MainActivity.kt`);
426
+ await fs3.ensureDir(path3.dirname(file));
427
+ await fs3.writeFile(file, main);
428
+ }
429
+ async cmd(proj) {
430
+ if (await fs3.pathExists(path3.join(proj, "gradlew"))) {
431
+ return "./gradlew";
432
+ }
433
+ if (await this.has("gradle")) {
434
+ return "gradle";
435
+ }
436
+ return null;
437
+ }
438
+ async has(cmd) {
439
+ try {
440
+ await execa(cmd, ["--version"]);
441
+ return true;
442
+ } catch {
443
+ return false;
444
+ }
445
+ }
446
+ apk(options) {
447
+ return options.mode === "native" ? "app-native.apk" : "app.apk";
448
+ }
449
+ safe(name) {
450
+ return name.replace(/[^a-z0-9._-]/gi, "-");
451
+ }
452
+ async pkg() {
261
453
  try {
262
454
  const pkg = await fs3.readJson("package.json");
263
455
  return {
264
- id: pkg.androidAppId || pkg.name.replace(/[^a-z0-9]/gi, ".").toLowerCase(),
456
+ id: pkg.androidAppId || this.id(pkg.name),
265
457
  name: pkg.name,
266
458
  version: pkg.version,
267
459
  description: pkg.description
@@ -274,13 +466,17 @@ public class MainActivity extends Activity {
274
466
  };
275
467
  }
276
468
  }
469
+ id(name) {
470
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, ".").replace(/^\.+|\.+$/g, "").replace(/\.\.+/g, ".");
471
+ return `com.tablejs.${slug || "app"}`;
472
+ }
277
473
  };
278
474
 
279
475
  // src/builder.ts
280
476
  async function build(platform, options = {}) {
281
477
  const opts = {
282
- platform,
283
478
  outDir: options.outDir || "dist",
479
+ mode: options.mode || "webview",
284
480
  minify: options.minify ?? true,
285
481
  sourcemap: options.sourcemap ?? false
286
482
  };
@@ -294,21 +490,30 @@ async function build(platform, options = {}) {
294
490
  }
295
491
  await buildPlatform(platform, opts);
296
492
  }
297
- async function buildPlatform(platform, options) {
493
+ async function buildPlatform(platform, base) {
494
+ const options = {
495
+ ...base,
496
+ dir: out(base.outDir, platform, base.mode),
497
+ platform
498
+ };
298
499
  const adapter = getAdapter(platform);
299
- await viteBuild({
300
- build: {
301
- outDir: `${options.outDir}/${platform}`,
302
- minify: options.minify,
303
- sourcemap: options.sourcemap,
304
- emptyOutDir: true
305
- },
306
- define: {
307
- __TABLE_PLATFORM__: JSON.stringify(platform)
308
- }
309
- });
500
+ if (web(platform, options.mode)) {
501
+ await viteBuild({
502
+ build: {
503
+ outDir: dir(options),
504
+ minify: options.minify,
505
+ sourcemap: options.sourcemap,
506
+ emptyOutDir: true
507
+ },
508
+ define: {
509
+ __TABLE_PLATFORM__: JSON.stringify(platform)
510
+ }
511
+ });
512
+ } else {
513
+ await fs4.emptyDir(options.dir);
514
+ }
310
515
  await adapter.build(options);
311
- const packagePath = await adapter.package(`${options.outDir}/${platform}`);
516
+ const packagePath = await adapter.package(options);
312
517
  console.log(`\u2713 ${adapter.name} package created: ${packagePath}`);
313
518
  }
314
519
  function getAdapter(platform) {
@@ -323,6 +528,21 @@ function getAdapter(platform) {
323
528
  throw new Error(`Unknown platform: ${platform}`);
324
529
  }
325
530
  }
531
+ function out(root, platform, mode) {
532
+ if (platform === "android" && mode === "native") {
533
+ return `${root}/android-native`;
534
+ }
535
+ return `${root}/${platform}`;
536
+ }
537
+ function dir(options) {
538
+ if (options.platform === "android") {
539
+ return `${options.dir}/www`;
540
+ }
541
+ return options.dir;
542
+ }
543
+ function web(platform, mode) {
544
+ return platform !== "android" || mode === "webview";
545
+ }
326
546
  export {
327
547
  AndroidAdapter,
328
548
  TizenAdapter,