Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,15 @@
"pattern": "."
}
],
"editor.gotoLocation.alternativeDeclarationCommand": "editor.action.revealDefinition",
"editor.gotoLocation.alternativeDefinitionCommand": "editor.action.revealDefinition",
"editor.gotoLocation.alternativeTypeDefinitionCommand": "editor.action.revealDefinition",
"editor.selectionHighlight": false,
"files.autoSave": "onFocusChange",
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.quickSuggestions": {
"other": "on",
"comments": "off",
"strings": "on"
},
"oxc.enable": true,

"files.readonlyInclude": {
"index.js": true,
"index.d.ts": true,
"project-detector.wasi-browser.js": true,
"project-detector.wasi.cjs": true,
"browser.js": true,
"wasi-worker-browser.mjs": true,
"wasi-worker.mjs": true,
"**/dist/**/*": true,
"**/node_modules/**/*": true
}
Expand Down
14 changes: 0 additions & 14 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
extern crate napi_build;

use std::env;

fn main() {
napi_build::setup();

// 仅当显式开启时才在 Linux GNU 的 arm64/armv7 目标上链接 libbsd,避免交叉工具链找不到 -lbsd
// 通过设置环境变量 LINK_LIBBSD=1 开启
if env::var("LINK_LIBBSD").map(|v| v == "1").unwrap_or(false) {
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();

if target_os == "linux" && target_env == "gnu" && (target_arch == "aarch64" || target_arch == "arm") {
println!("cargo:rustc-link-lib=bsd");
}
}
}
120 changes: 86 additions & 34 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
/* auto-generated by NAPI-RS */
/* eslint-disable */
export declare class AppScope {
static from(project: Project): AppScope | null
getUri(): Uri
getConfigUri(): Uri
getConfigContent(): string
getParsedConfigContent(): any
reload(): void
}

export declare class ElementDirectory {
static from(resourceDirectory: ResourceDirectory): ElementDirectory | null
getUri(): Uri
Expand Down Expand Up @@ -91,53 +100,72 @@ export declare class QualifierUtils {
/** * Check if the mcc is a valid MCC code with value.
*/
static isMcc(mcc: number): boolean
/** * Check if the mcc is a valid MCC code with string `mcc`.
/**
* Check if the mcc is a valid MCC code with string `mcc`.
*
* For example: "mcc310" => true, "mcc3100" => false
*/
static isMccCode(mcc: string): boolean
/** * Check if the language code is a valid language code.
static isMccCode(mcc: `mcc${number}` | (string & {})): boolean
/**
* Check if the language code is a valid language code.
*
* For example: "en" => true, "en-US" => false
*/
static isLanguageCode(languageCode: string): boolean
/** * Check if the device type is a valid device type.
* - phone
* - tablet
* - tv
* - car
* - wearable
* - 2in1
*/
static isDeviceType(deviceType: string): boolean
/** * Check if the color mode is a valid color mode.
* - dark
* - light
*/
static isColorMode(colorMode: string): boolean
/** * Check if the mnc is a valid MNC code with value.
/**
* Check if the device type is a valid device type.
*
* - `phone`
* - `tablet`
* - `tv`
* - `car`
* - `wearable`
* - `2in1`
*/
static isDeviceType(deviceType: 'phone' | 'tablet' | 'tv' | 'car' | 'wearable' | '2in1' | (string & {})): boolean
/**
* Check if the color mode is a valid color mode.
*
* - `dark`
* - `light`
*/
static isColorMode(colorMode: 'dark' | 'light' | (string & {})): boolean
/**
* Check if the mnc is a valid MNC code with value.
*
* For example: 00 => true, 000 => false
*/
static isMnc(mnc: number, mcc: number): boolean
/** * Check if the mnc is a valid MNC code with string `mnc` and `mcc`.
/**
* Check if the mnc is a valid MNC code with string `mnc` and `mcc`.
*
* For example: "mnc00" => true, "mnc000" => false
*/
static isMncCode(mnc: string, mcc: number): boolean
/** * Check if the region code is a valid region code.
/**
* Check if the region code is a valid region code.
*
* For example: "CN" => true, "US" => true, "AAA" => false
*/
static isRegionCode(regionCode: string): boolean
/** * Check if the orientation is a valid orientation.
* - vertical
* - horizontal
*/
static isOrientation(orientation: string): boolean
/** * Check if the screen density is a valid screen density.
* - sdpi
* - mdpi
* - ldpi
* - xldpi
* - xxldpi
* - xxxldpi
*/
static isScreenDensity(screenDensity: string): boolean
/**
* Check if the orientation is a valid orientation.
*
* - `vertical`
* - `horizontal`
*/
static isOrientation(orientation: 'vertical' | 'horizontal' | (string & {})): boolean
/**
* Check if the screen density is a valid screen density.
*
* - `sdpi`
* - `mdpi`
* - `ldpi`
* - `xldpi`
* - `xxldpi`
* - `xxxldpi`
*/
static isScreenDensity(screenDensity: 'sdpi' | 'mdpi' | 'ldpi' | 'xldpi' | 'xxldpi' | 'xxxldpi' | (string & {})): boolean
/** * Analyze the qualifier and return the qualifier list.
*
* 限定词目录由一个或多个表征应用场景或设备特征的限定词组合而成,限定词包括移动国家码和移动网络码、语言、文字、国家或地区、横竖屏、设备类型、颜色模式和屏幕密度,限定词之间通过下划线(_)或者中划线(-)连接。开发者在创建限定词目录时,需要遵守如下限定词目录命名规则。
Expand Down Expand Up @@ -166,9 +194,28 @@ export declare class ResfileDirectory {

export declare class Resource {
static findAll(product: Product): Array<Resource>
static fromAppScope(appScope: AppScope): Resource | null
static create(product: Product, resourceUri: string): Resource | null
/**
* If the resource created by {@linkcode Product}, this method will return the product.
*
* @throw If the resource is not created by {@linkcode Product}, this method will throw an error.
*/
getProduct(): Product
/**
* If the resource created by {@linkcode AppScope}, this method will return the app scope.
*
* @throw If the resource is not created by {@linkcode AppScope}, this method will throw an error.
*/
getAppScope(): AppScope
getUri(): Uri
/**
* Get the type of current resource.
*
* - If created by {@link Product}, return {@link ResourceType.Product}.
* - If created by {@link AppScope}, return {@link ResourceType.AppScope}.
*/
get resourceType(): ResourceType
}

export declare class ResourceDirectory {
Expand Down Expand Up @@ -216,3 +263,8 @@ export declare const enum QualifierType {
LanguageCode = 'LanguageCode',
DeviceType = 'DeviceType'
}

export declare const enum ResourceType {
Product = 0,
AppScope = 1
}
41 changes: 21 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
// @ts-nocheck
/* auto-generated by NAPI-RS */

import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)
const __dirname = new URL('.', import.meta.url).pathname
const { createRequire } = require('node:module')
require = createRequire(__filename)

const { readFileSync } = require('node:fs')
let nativeBinding = null
Expand Down Expand Up @@ -557,20 +556,22 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { ElementDirectory, ElementJsonFile, ElementJsonFileReference, MediaDirectory, Module, Product, ProfileDirectory, Project, ProjectDetector, QualifierUtils, RawfileDirectory, ResfileDirectory, Resource, ResourceDirectory, Uri, QualifierType } = nativeBinding
export { ElementDirectory }
export { ElementJsonFile }
export { ElementJsonFileReference }
export { MediaDirectory }
export { Module }
export { Product }
export { ProfileDirectory }
export { Project }
export { ProjectDetector }
export { QualifierUtils }
export { RawfileDirectory }
export { ResfileDirectory }
export { Resource }
export { ResourceDirectory }
export { Uri }
export { QualifierType }
module.exports = nativeBinding
module.exports.AppScope = nativeBinding.AppScope
module.exports.ElementDirectory = nativeBinding.ElementDirectory
module.exports.ElementJsonFile = nativeBinding.ElementJsonFile
module.exports.ElementJsonFileReference = nativeBinding.ElementJsonFileReference
module.exports.MediaDirectory = nativeBinding.MediaDirectory
module.exports.Module = nativeBinding.Module
module.exports.Product = nativeBinding.Product
module.exports.ProfileDirectory = nativeBinding.ProfileDirectory
module.exports.Project = nativeBinding.Project
module.exports.ProjectDetector = nativeBinding.ProjectDetector
module.exports.QualifierUtils = nativeBinding.QualifierUtils
module.exports.RawfileDirectory = nativeBinding.RawfileDirectory
module.exports.ResfileDirectory = nativeBinding.ResfileDirectory
module.exports.Resource = nativeBinding.Resource
module.exports.ResourceDirectory = nativeBinding.ResourceDirectory
module.exports.Uri = nativeBinding.Uri
module.exports.QualifierType = nativeBinding.QualifierType
module.exports.ResourceType = nativeBinding.ResourceType
10 changes: 10 additions & 0 deletions mock/harmony-project-1/AppScope/app.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"app": {
"bundleName": "cc.naily.demo1",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"string": [
{
"name": "app_name",
"value": "YesPlayNext"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"layered-image":
{
"background" : "$media:background",
"foreground" : "$media:foreground"
}
}
10 changes: 10 additions & 0 deletions mock/harmony-project-2/AppScope/app.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"app": {
"bundleName": "cc.naily.demo2",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:layered_image",
"label": "$string:app_name"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"string": [
{
"name": "app_name",
"value": "YesPlayNext"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"layered-image":
{
"background" : "$media:background",
"foreground" : "$media:foreground"
}
}
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"name": "@arkts/project-detector",
"type": "module",
"version": "1.1.3-alpha.29",
"packageManager": "pnpm@10.18.0",
"description": "A native Rust-based project detector for HarmonyOS/OpenHarmony projects",
Expand Down Expand Up @@ -78,7 +77,7 @@
"scripts": {
"artifacts": "napi artifacts",
"bench": "node --import @oxc-node/core/register benchmark/bench.ts",
"build": "napi build --esm --platform --release",
"build": "napi build --platform --release",
"postbuild": "tsdown --config-loader unconfig",
"build:debug": "napi build --platform",
"format:toml": "taplo format",
Expand Down
2 changes: 2 additions & 0 deletions project-detector.wasi-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const {
},
})
export default __napiModule.exports
export const AppScope = __napiModule.exports.AppScope
export const ElementDirectory = __napiModule.exports.ElementDirectory
export const ElementJsonFile = __napiModule.exports.ElementJsonFile
export const ElementJsonFileReference = __napiModule.exports.ElementJsonFileReference
Expand All @@ -72,3 +73,4 @@ export const Resource = __napiModule.exports.Resource
export const ResourceDirectory = __napiModule.exports.ResourceDirectory
export const Uri = __napiModule.exports.Uri
export const QualifierType = __napiModule.exports.QualifierType
export const ResourceType = __napiModule.exports.ResourceType
2 changes: 2 additions & 0 deletions project-detector.wasi.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule
},
})
module.exports = __napiModule.exports
module.exports.AppScope = __napiModule.exports.AppScope
module.exports.ElementDirectory = __napiModule.exports.ElementDirectory
module.exports.ElementJsonFile = __napiModule.exports.ElementJsonFile
module.exports.ElementJsonFileReference = __napiModule.exports.ElementJsonFileReference
Expand All @@ -124,3 +125,4 @@ module.exports.Resource = __napiModule.exports.Resource
module.exports.ResourceDirectory = __napiModule.exports.ResourceDirectory
module.exports.Uri = __napiModule.exports.Uri
module.exports.QualifierType = __napiModule.exports.QualifierType
module.exports.ResourceType = __napiModule.exports.ResourceType
26 changes: 26 additions & 0 deletions src/node/app-scope.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Uri } from '../../index'
import type { Project } from './project'
import type { ProjectDetector } from './project-detector'
import { signal } from 'alien-signals'
import { AppScope as RustAppScope } from '../../index'
import { DisposableSignal } from './types'

export interface AppScope extends RustAppScope {}

export namespace AppScope {
export function from(project: Project): DisposableSignal<AppScope | null> {
const appScope = signal<AppScope | null>(RustAppScope.from(project.getUnderlyingProject()))

const handle = (_event: keyof ProjectDetector.EventMap, uri: Uri) => {
if (project.getBuildProfileUri().isEqual(uri) || project.getUri().isEqual(uri)) {
appScope(RustAppScope.from(project.getUnderlyingProject()))
}
else if (uri.fsPath.endsWith('app.json5')) {
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comparison uri.fsPath.endsWith('app.json5') is fragile and could match unintended files. Consider using a more robust check that ensures the file is specifically in the AppScope directory, such as checking the full path or using uri.isEqual() with the expected app.json5 URI.

Suggested change
else if (uri.fsPath.endsWith('app.json5')) {
else if (uri.isEqual(project.getAppJson5Uri())) {

Copilot uses AI. Check for mistakes.
appScope(RustAppScope.from(project.getUnderlyingProject()))
}
}

project.getProjectDetector().on('*', handle)
return DisposableSignal.fromSignal(appScope, () => project.getProjectDetector().off('*', handle))
}
}
1 change: 1 addition & 0 deletions src/node/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { type Qualifier, QualifierType, QualifierUtils, Uri } from '../../index'
export * from './app-scope'
export * from './element-directory'
export * from './files/element-json-file'
export * from './interfaces'
Expand Down
Loading