diff --git a/modules/lib/build.gradle b/modules/lib/build.gradle index 4d940908fc1..3f2eb0a4d5a 100644 --- a/modules/lib/build.gradle +++ b/modules/lib/build.gradle @@ -28,7 +28,9 @@ check.dependsOn lint /* Configure & Build */ -task typescript( type: NpmTask, dependsOn: npmInstall ) { +task typescript( type: NpmTask ) { + dependsOn 'npmInstall', 'prepareToPublish' + description = 'Create JS and DTS files from TS' args = ['run', 'build'] @@ -102,6 +104,11 @@ task prepareCoreToPublish( type: Copy ) { from "$coreDir/README.md" from "$coreDir/index.d.ts" + def dependencyLines = [ + ' "dependencies": {', + ' "@enonic-types/global": "' + version + '"', + ' }' + ] from( 'package.template.json' ) { filter { line -> line @@ -110,6 +117,7 @@ task prepareCoreToPublish( type: Copy ) { .replaceAll( '%FULL_NAME%', 'core' ) .replaceAll( '%FILE_NAME%', 'index' ) .replaceAll( '%DESCRIPTION%', 'Shared type definition for core libraries.' ) + .replaceAll( / "dependencies": \{\}/, dependencyLines.join( '\n' ) ) } rename '.+', 'package.json' } diff --git a/modules/lib/core/globalModifying.d.ts b/modules/lib/core/globalModifying.d.ts deleted file mode 100644 index e06d954402f..00000000000 --- a/modules/lib/core/globalModifying.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -// NOTE: A global modifying module must have at least one export - -export declare type ComponentDescriptor = `${string}:${string}`; - -export declare interface NestedRecord { - [name: PropertyKey]: NestedRecord | unknown -} - -declare global { - interface XpBeans {} - interface XpLayoutMap { - [layoutDescriptor: ComponentDescriptor]: NestedRecord; - } - interface XpLibraries {} - interface XpPageMap { - [pageDescriptor: ComponentDescriptor]: NestedRecord; - } - interface XpPartMap { - [partDescriptor: ComponentDescriptor]: NestedRecord; - } - interface XpXData { - [key: string]: Record>; - } -} diff --git a/modules/lib/core/globals.d.ts b/modules/lib/core/globals.d.ts deleted file mode 100644 index 96df6c52cda..00000000000 --- a/modules/lib/core/globals.d.ts +++ /dev/null @@ -1,122 +0,0 @@ -import type { ResourceKey } from './resource'; - -export declare interface App { - /** - * The name of the application. - * - * @type string - */ - name: string; - /** - * Version of the application. - * - * @type string - */ - version: string; - /** - * Values from the application’s configuration file. - * This can be set using $XP_HOME/config/.cfg. - * Every time the configuration is changed the app is restarted. - * - * @type Object - */ - config: Record; -} - -export declare interface Log { - /** - * Log debug message. - * - * @param {Array} args... logging arguments. - */ - debug: (...args: unknown[]) => void; - - /** - * Log info message. - * - * @param {Array} args... logging arguments. - */ - info: (...args: unknown[]) => void; - - /** - * Log warning message. - * - * @param {Array} args... logging arguments. - */ - warning: (...args: unknown[]) => void; - - /** - * Log error message. - * - * @param {Array} args... logging arguments. - */ - error: (...args: unknown[]) => void; -} - -export declare interface ScriptValue { - isArray(): boolean; - - isObject(): boolean; - - isValue(): boolean; - - isFunction(): boolean; - - getValue(): unknown; - - getKeys(): string[]; - - hasMember(key: string): boolean; - - getMember(key: string): ScriptValue; - - getArray(): ScriptValue[]; - - getMap(): Record; - - getList(): object[]; -} - -export declare type NewBean = (bean: Bean) => - Bean extends keyof XpBeans ? XpBeans[Bean] : T; - -export declare interface DoubleUnderscore { - /** - * Creates a new JavaScript bean that wraps the given Java class and makes its methods available to be called from JavaScript. - */ - newBean: NewBean; - /** - * Converts arrays or complex Java objects to JSON. - * @param value Value to convert - */ - toNativeObject: (value: T) => T; - /** - * Converts JSON to a Java Map structure that can be used as parameters to a Java method on a bean created with newBean. - * @param value Value to convert - */ - toScriptValue: (value: T) => ScriptValue; - /** - * Add a disposer that is called when the app is stopped. - * @param callback Function to call - */ - disposer: (callback: (...args: unknown[]) => unknown) => void; - /** - * Converts a JavaScript variable that is undefined to a Java null object. - * If the JavaScript variable is defined, it is returned as is. - * @param value Value to convert - */ - nullOrValue: (value: T) => T | null | undefined; - - /** - * Doc registerMock. - * - * @param name Name of mock. - * @param value Value to register. - */ - registerMock: (name: string, value: object) => void -} - -export declare type XpRequire = (path: Key) => - Key extends keyof XpLibraries ? XpLibraries[Key] : unknown; - -export declare type Resolve = (path: string) => ResourceKey; \ No newline at end of file diff --git a/modules/lib/core/index.d.ts b/modules/lib/core/index.d.ts index ae5f6175fe6..78a74bbe5da 100644 --- a/modules/lib/core/index.d.ts +++ b/modules/lib/core/index.d.ts @@ -1,27 +1,27 @@ -// This also makes sure XpLayoutMap, etc are available. -import type { - ComponentDescriptor, - NestedRecord, -} from './globalModifying'; - -export type { - ComponentDescriptor, - NestedRecord, -} from './globalModifying'; -export type { - App, - DoubleUnderscore, - Log, - NewBean, - Resolve, - ScriptValue, - XpRequire, -} from './globals'; -export type { - ByteSource, - Resource, - ResourceKey, -} from './resource'; +export type { ResourceKey } from '@enonic-types/global'; + +export type ComponentDescriptor = `${string}:${string}`; + +export interface NestedRecord { + [name: PropertyKey]: NestedRecord | unknown +} + +declare global { + interface XpBeans {} + interface XpLayoutMap { + [layoutDescriptor: ComponentDescriptor]: NestedRecord; + } + interface XpLibraries {} + interface XpPageMap { + [pageDescriptor: ComponentDescriptor]: NestedRecord; + } + interface XpPartMap { + [partDescriptor: ComponentDescriptor]: NestedRecord; + } + interface XpXData { + [key: string]: Record>; + } +} export type UserKey = `user:${string}:${string}`; export type GroupKey = `group:${string}:${string}`; @@ -220,6 +220,26 @@ export interface Content< fragment?: Type extends 'portal:fragment' ? _Component : never; } +// Compliant with npm module ts-brand +type Brand< + Base, + Branding +> = Base & { + '__type__': Branding +}; + +export type ByteSource = Brand; + +export interface Resource { + getSize(): number; + + getTimestamp(): number; + + getStream(): ByteSource; + + exists(): boolean; +} + // // DSL QUERIES // @@ -328,9 +348,9 @@ export type QueryDsl = { exists: ExistsDslExpression; }; -type SortDirection = 'ASC' | 'DESC'; +export type SortDirection = 'ASC' | 'DESC'; -type DistanceUnit = +export type DistanceUnit = | 'm' | 'meters' | 'in' diff --git a/modules/lib/core/resource.d.ts b/modules/lib/core/resource.d.ts deleted file mode 100644 index 1461ac6cf06..00000000000 --- a/modules/lib/core/resource.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Compliant with npm module ts-brand -type Brand< - Base, - Branding -> = Base & { - '__type__': Branding -}; - -export type ByteSource = Brand; - -export declare interface Resource { - getSize(): number; - - getTimestamp(): number; - - getStream(): ByteSource; - - exists(): boolean; -} - -export declare interface ResourceKey { - getApplicationKey(): string; - getPath(): string; - getUri(): string; - isRoot(): boolean; - getName(): string; - getExtension(): string; -} diff --git a/modules/lib/global.d.ts b/modules/lib/global.d.ts index a4bcf81a15a..915868a6d8d 100644 --- a/modules/lib/global.d.ts +++ b/modules/lib/global.d.ts @@ -1,94 +1,240 @@ -import type { - App, - DoubleUnderscore, - Log, - Resolve, - XpRequire -} from '@enonic-types/core'; - -/** - * The globally available app object holds information about the contextual application. - * @example - * var nameVersion = app.name + ' v' + app.version; - * - * @global - * @namespace - */ -declare const app: App; - -/** - * Logging functions. - * - * @example - * // Log with simple message - * log.debug('My log message'); - * - * @example - * // Log with placeholders - * log.info('My %s message with %s', 'log', 'placeholders'); - * - * @example - * // Log a JSON object - * log.warning('My JSON: %s', {a: 1}); - * - * @example - * // Log JSON object using string - * log.error('My JSON: %s', JSON.stringify({a: 1}, null, 2)); - * - * @global - * @namespace - */ -declare const log: Log; - -/** - * Javascript to Java bridge functions. - * - * @example - * var bean = __.newBean('com.enonic.xp.MyJavaUtils'); - * - * @example - * return __.toNativeObject(bean.findArray(arrayName)); - * - * @global - * @namespace - */ -declare const __: DoubleUnderscore; - -/** - * This globally available function will load a JavaScript file and return the exports as objects. - * The function implements parts of the `CommonJS Modules Specification`. - * - * @example - * // Require relative to this - * var other = require('./other.js'); - * - * @example - * // Require absolute - * var other = require('/path/to/other.js'); - * - * @example - * // Require without .js extension - * var other = require('./other'); - * - * @param {string} path Path for javascript file (relative or absolute and .js ending is optional). - * @returns {object} Exports from loaded javascript. - * @global - */ -declare const require: XpRequire; - -/** - * Resolves a path to another file. Can use relative or absolute path. - * - * @example - * // Resolve relative to this - * var path = resolve('./other.html'); - * - * @example - * // Resolve absolute - * var path = resolve('/path/to/other.html'); - * - * @param {string} path Path to resolve. - * @returns {*} Reference to an object. - * @global - */ -declare const resolve: Resolve; +//────────────────────────────────────────────────────────────────────────────── +// Type definitions +// Single source of truth for type definitions that are both global and exported +//────────────────────────────────────────────────────────────────────────────── +type NewBeanDefinition = (bean: Bean) => + Bean extends keyof XpBeans ? XpBeans[Bean] : T; + +interface ScriptValueDefinition { + isArray(): boolean; + + isObject(): boolean; + + isValue(): boolean; + + isFunction(): boolean; + + getValue(): unknown; + + getKeys(): string[]; + + hasMember(key: string): boolean; + + getMember(key: string): ScriptValueDefinition; + + getArray(): ScriptValueDefinition[]; + + getMap(): Record; + + getList(): object[]; +} + +//────────────────────────────────────────────────────────────────────────────── +// Declare global types +//────────────────────────────────────────────────────────────────────────────── +declare global { + type NewBean = NewBeanDefinition; + type ScriptValue = ScriptValueDefinition; + + /** + * The globally available app object holds information about the contextual application. + * @example + * var nameVersion = app.name + ' v' + app.version; + * + * @global + * @namespace + */ + declare const app: App; + + /** + * Logging functions. + * + * @example + * // Log with simple message + * log.debug('My log message'); + * + * @example + * // Log with placeholders + * log.info('My %s message with %s', 'log', 'placeholders'); + * + * @example + * // Log a JSON object + * log.warning('My JSON: %s', {a: 1}); + * + * @example + * // Log JSON object using string + * log.error('My JSON: %s', JSON.stringify({a: 1}, null, 2)); + * + * @global + * @namespace + */ + declare const log: Log; + + /** + * Javascript to Java bridge functions. + * + * @example + * var bean = __.newBean('com.enonic.xp.MyJavaUtils'); + * + * @example + * return __.toNativeObject(bean.findArray(arrayName)); + * + * @global + * @namespace + */ + declare const __: DoubleUnderscore; + + /** + * This globally available function will load a JavaScript file and return the exports as objects. + * The function implements parts of the `CommonJS Modules Specification`. + * + * @example + * // Require relative to this + * var other = require('./other.js'); + * + * @example + * // Require absolute + * var other = require('/path/to/other.js'); + * + * @example + * // Require without .js extension + * var other = require('./other'); + * + * @param {string} path Path for javascript file (relative or absolute and .js ending is optional). + * @returns {object} Exports from loaded javascript. + * @global + */ + declare const require: XpRequire; + + /** + * Resolves a path to another file. Can use relative or absolute path. + * + * @example + * // Resolve relative to this + * var path = resolve('./other.html'); + * + * @example + * // Resolve absolute + * var path = resolve('/path/to/other.html'); + * + * @param {string} path Path to resolve. + * @returns {*} Reference to an object. + * @global + */ + declare const resolve: Resolve; + +} + +//────────────────────────────────────────────────────────────────────────────── +// Exported types +//────────────────────────────────────────────────────────────────────────────── +export type NewBean = NewBeanDefinition; +export type ScriptValue = ScriptValueDefinition; + +export interface App { + /** + * The name of the application. + * + * @type string + */ + name: string; + /** + * Version of the application. + * + * @type string + */ + version: string; + /** + * Values from the application’s configuration file. + * This can be set using $XP_HOME/config/.cfg. + * Every time the configuration is changed the app is restarted. + * + * @type Object + */ + config: Record; +} + +export interface DoubleUnderscore { + /** + * Creates a new JavaScript bean that wraps the given Java class and makes its methods available to be called from JavaScript. + */ + newBean: NewBean; + /** + * Converts arrays or complex Java objects to JSON. + * @param value Value to convert + */ + toNativeObject: (value: T) => T; + /** + * Converts JSON to a Java Map structure that can be used as parameters to a Java method on a bean created with newBean. + * @param value Value to convert + */ + toScriptValue: (value: T) => ScriptValue; + /** + * Add a disposer that is called when the app is stopped. + * @param callback Function to call + */ + disposer: (callback: (...args: unknown[]) => unknown) => void; + /** + * Converts a JavaScript variable that is undefined to a Java null object. + * If the JavaScript variable is defined, it is returned as is. + * @param value Value to convert + */ + nullOrValue: (value: T) => T | null | undefined; + + /** + * Doc registerMock. + * + * @param name Name of mock. + * @param value Value to register. + */ + registerMock: (name: string, value: object) => void +} + +export interface Log { + /** + * Log debug message. + * + * @param {Array} args... logging arguments. + */ + debug: (...args: unknown[]) => void; + + /** + * Log info message. + * + * @param {Array} args... logging arguments. + */ + info: (...args: unknown[]) => void; + + /** + * Log warning message. + * + * @param {Array} args... logging arguments. + */ + warning: (...args: unknown[]) => void; + + /** + * Log error message. + * + * @param {Array} args... logging arguments. + */ + error: (...args: unknown[]) => void; +} + +export interface ResourceKey { + getApplicationKey(): string; + getPath(): string; + getUri(): string; + isRoot(): boolean; + getName(): string; + getExtension(): string; +} + +export type Resolve = (path: string) => ResourceKey; + +export type XpRequire = (path: Key) => + Key extends keyof XpLibraries ? XpLibraries[Key] : unknown; + +//────────────────────────────────────────────────────────────────────────────── +// Making sure the file is a module +//────────────────────────────────────────────────────────────────────────────── +export {}; diff --git a/modules/lib/package-lock.json b/modules/lib/package-lock.json index 186bf6e79bc..bba3dbd3ff2 100644 --- a/modules/lib/package-lock.json +++ b/modules/lib/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "license": "Apache-2.0", "devDependencies": { - "@enonic-types/core": "./core", "@enonic/eslint-config": "^0.1.0", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", @@ -21,7 +20,7 @@ "core": { "name": "@enonic-types/core", "version": "0.0.1", - "dev": true + "extraneous": true }, "node_modules/@babel/parser": { "version": "7.18.11", @@ -34,10 +33,6 @@ "node": ">=6.0.0" } }, - "node_modules/@enonic-types/core": { - "resolved": "core", - "link": true - }, "node_modules/@enonic/eslint-config": { "version": "0.1.0", "integrity": "sha512-h23kujMUdfHl22jMUF7pLDNiSSnPMsROlpOeMbpO6wN2hH3pZQ67QduSLG3+epVc7zc4463TauQOxGV7gJ85xw==", @@ -1678,9 +1673,6 @@ "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", "dev": true }, - "@enonic-types/core": { - "version": "file:core" - }, "@enonic/eslint-config": { "version": "0.1.0", "integrity": "sha512-h23kujMUdfHl22jMUF7pLDNiSSnPMsROlpOeMbpO6wN2hH3pZQ67QduSLG3+epVc7zc4463TauQOxGV7gJ85xw==", diff --git a/modules/lib/package.json b/modules/lib/package.json index 1120e8d2a24..bf90c731e81 100644 --- a/modules/lib/package.json +++ b/modules/lib/package.json @@ -23,7 +23,6 @@ "homepage": "https://github.com/enonic/xp/tree/master#readme", "devDependencies": { "@enonic/eslint-config": "^0.1.0", - "@enonic-types/core": "./core", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", "eslint": "^8.15.0", diff --git a/modules/lib/tsconfig.build.json b/modules/lib/tsconfig.build.json index 85ba3bca9f7..e9d60150367 100644 --- a/modules/lib/tsconfig.build.json +++ b/modules/lib/tsconfig.build.json @@ -1,9 +1,11 @@ -{ +{ // This file is used to build typescript declaration files? "extends": "./tsconfig", "include": [ "./lib-*/build/typescript/", "./global.d.ts" ], + // Since this file extends tsconfig.json which excludes "./**/build", this + // file needs it's own exclude. "exclude": [ "./lib-*/*.ts" ] diff --git a/modules/lib/tsconfig.json b/modules/lib/tsconfig.json index 5765439ac04..a77d3bb0bdc 100644 --- a/modules/lib/tsconfig.json +++ b/modules/lib/tsconfig.json @@ -1,17 +1,46 @@ -{ +{ // This file will be used by the IDE. "compilerOptions": { + // NOTE: This is probably correct since we're still building for Nashorn. "lib": ["es5"], + + // TODO: This only matters at buildtime, perhaps move to + // tsconfig.build.json "removeComments": false, + + // The target setting changes which JS features are downleveled and + // which are left intact. Changing target also changes the default value + // of lib. You may “mix and match” target and lib settings as desired, + // but you could just set target for convenience. + // NOTE: This is probably correct since we're still building for Nashorn. "target": "es5", + + // NOTE: This is probably correct since we're still building for Nashorn. "module": "commonjs", + + // TODO: Maybe use "node16" or "bundler" since we're not using file + // extensions in the imports? + // 'node10' (previously called 'node') for Node.js versions older than + // v10, which only support CommonJS require. You probably won’t need to + // use node10 in modern code. "moduleResolution": "node", - "typeRoots": [ - "node_modules/@enonic-types" - ], + + "paths": { + // So they can be imported from without installing old ones into + // node_modules: + "@enonic-types/global": ["./global.d.ts"], + "@enonic-types/core": ["./core/index.d.ts"], + }, + + // TODO: This only matters at buildtime, perhaps move to + // tsconfig.build.json "sourceMap": false, + "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, + + // TODO deprecated TypeScript 5.5 + // Also why was it ever here? "noImplicitUseStrict": true }, "exclude": [