diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..504afef81
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+package-lock.json
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 000000000..4ef133f11
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,2 @@
+/node_modules/
+package-lock.json
diff --git a/auto/make b/auto/make
index 4ec533fee..871354ff5 100644
--- a/auto/make
+++ b/auto/make
@@ -19,6 +19,11 @@ NJS_STATIC_LINK = ${AR} -r -c
NJS_LINK = ${CC} ${NJS_LD_OPT}
NJS_CFLAGS = ${NJS_CFLAGS} ${NJS_CC_OPT} ${CFLAGS}
+NJS_VER = $(grep NJS_VERSION src/njs.h | sed -e 's/.*"\(.*\)".*/\1/')
+NJS_TYPES_VER ?= \$(NJS_VER)
+
+NPM = npm
+
default: $NJS_DEFAULT_TARGET
END
@@ -260,21 +265,50 @@ benchmark: $NJS_BUILD_DIR/njs_auto_config.h \\
$NJS_BUILD_DIR/njs_benchmark
-ts:
- mkdir -p $NJS_BUILD_DIR/ts
- cp nginx/ts/*.ts $NJS_BUILD_DIR/ts/
- cp src/ts/*.ts $NJS_BUILD_DIR/ts/
+$NJS_BUILD_DIR/ts/package.json: $NJS_TS_SRCS
+ cp -fr ts $NJS_BUILD_DIR/
+ cp LICENSE $NJS_BUILD_DIR/ts/
+ sed 's/\("version":\s*\)"\([^"]\+\)"/\1"\$(NJS_TYPES_VER)"/' \\
+ ts/package.json > $NJS_BUILD_DIR/ts/package.json
+
+$NJS_BUILD_DIR/ts/node_modules: $NJS_BUILD_DIR/ts/package.json
+ cd $NJS_BUILD_DIR/ts && \$(NPM) install
+ touch $NJS_BUILD_DIR/ts/node_modules
+
+$NJS_BUILD_DIR/njs-types-\$(NJS_TYPES_VER).tgz: $NJS_BUILD_DIR/ts/package.json
+ hg id -i > $NJS_BUILD_DIR/ts/COMMITHASH || true
+ cd $NJS_BUILD_DIR && \$(NPM) pack ./ts
+
+$NJS_BUILD_DIR/test/ts/package.json: $NJS_TS_TEST_SRCS
+ mkdir -p $NJS_BUILD_DIR/test
+ cp -fr test/ts $NJS_BUILD_DIR/test/
+
+$NJS_BUILD_DIR/test/ts/node_modules: $NJS_BUILD_DIR/njs-types-\$(NJS_TYPES_VER).tgz $NJS_BUILD_DIR/test/ts/package.json
+ cd $NJS_BUILD_DIR/test/ts && \$(NPM) install --save-dev file:../../njs-types-\$(NJS_TYPES_VER).tgz
+ cd $NJS_BUILD_DIR/test/ts && \$(NPM) install
+ touch $NJS_BUILD_DIR/test/ts/node_modules
+
+.PHONY: ts
+ts: $NJS_BUILD_DIR/ts/package.json
+
+ts_lint: $NJS_BUILD_DIR/ts/node_modules
+ cd $NJS_BUILD_DIR/ts && \$(NPM) run lint
+
+ts_test: $NJS_BUILD_DIR/test/ts/node_modules
+ cd $NJS_BUILD_DIR/test/ts && \$(NPM) test
+
+ts_publish: ts_clean $NJS_BUILD_DIR/njs-types-\$(NJS_TYPES_VER).tgz
+ \$(NPM) publish $<
-ts_test: ts
- tsc ./test/ts/test.ts
+ts_clean:
+ rm -rf $NJS_BUILD_DIR/ts $NJS_BUILD_DIR/test/ts
dist:
- NJS_VER=`grep NJS_VERSION src/njs.h | sed -e 's/.*"\(.*\)".*/\1/'`; \\
- rm -rf njs-\$\${NJS_VER} \\
- && hg archive njs-\$\${NJS_VER}.tar.gz \\
- -p njs-\$\${NJS_VER} \\
+ rm -rf njs-\$(NJS_VER) \\
+ && hg archive njs-\$(NJS_VER).tar.gz \\
+ -p njs-\$(NJS_VER) \\
-X ".hg*" \\
- && echo njs-\$\${NJS_VER}.tar.gz done
+ && echo njs-\$(NJS_VER).tar.gz done
END
diff --git a/auto/sources b/auto/sources
index 418a4d611..22622b00a 100644
--- a/auto/sources
+++ b/auto/sources
@@ -71,3 +71,7 @@ NJS_TEST_SRCS=" \
src/test/njs_unit_test.c \
src/test/njs_benchmark.c \
"
+
+NJS_TS_SRCS=$(find ts/ -name "*.d.ts" -o -name "*.json" | xargs)
+
+NJS_TS_TEST_SRCS=$(find test/ts/ -name "*.ts" -o -name "*.json" | xargs)
diff --git a/test/ts/package.json b/test/ts/package.json
new file mode 100644
index 000000000..3daeea81c
--- /dev/null
+++ b/test/ts/package.json
@@ -0,0 +1,15 @@
+{
+ "private": true,
+ "name": "njs-types-test",
+ "version": "0.0.0",
+ "description": "Tests for njs TypeScript type definitions.",
+ "scripts": {
+ "test": "tsc"
+ },
+ "author": "NGINX, Inc.",
+ "license": "BSD-2-Clause",
+ "devDependencies": {
+ "njs-types": "file:../../ts",
+ "typescript": "~4.0.3"
+ }
+}
diff --git a/test/ts/test.ts b/test/ts/test.ts
index dbb8df7b7..ce7940ded 100644
--- a/test/ts/test.ts
+++ b/test/ts/test.ts
@@ -1,8 +1,3 @@
-///
-///
-///
-///
-
import fs from 'fs';
import qs from 'querystring';
import crypto from 'crypto';
@@ -15,9 +10,9 @@ function http_module(r: NginxHTTPRequest) {
s = 'ordinary string';
bs = String.bytesFrom('000000', 'hex');
- bs = s.toBytes();
+ var bs2: NjsByteString | null = s.toBytes();
bs = s.toUTF8();
- bs.fromBytes(null, null);
+ bs.fromBytes(undefined, undefined);
s = bs + '';
@@ -30,29 +25,30 @@ function http_module(r: NginxHTTPRequest) {
bs = r.args.x;
bs = r.args[1];
- s = r.args.x.fromUTF8();
+ var s2: string | null = r.args.x.fromUTF8();
s = r.args.x + '';
// r.headersIn
- r.headersIn['Accept'].fromBytes() == 'dddd';
+ r.headersIn['Accept']?.fromBytes() == 'dddd';
// r.headersOut
r.headersOut['Content-Type'] = 'text/plain';
// Warning: r.headersOut['Content-Type'] = ['a', 'b'];
r.headersOut['Connection'] = undefined;
- r.headersOut['Connection'] = null;
+
+ delete r.headersOut['Bar'];
r.headersOut['Set-Cookie'] = ['aaa', 'bbb'];
r.headersOut['Foo'] = ['aaa', 'bbb'];
- r.subrequest('/uri', reply => r.return(200, reply.headersOut["Location"]));
+ r.subrequest('/uri', reply => r.return(200, reply.headersOut["Location"] ?? ''));
// r.log
r.log(bs);
- r.log(r.headersOut['Connection']);
+ r.log(r.headersOut['Connection'] ?? '');
// r.variables
@@ -64,7 +60,7 @@ function http_module(r: NginxHTTPRequest) {
r.subrequest('/p/sub2', reply => r.return(reply.status));
r.subrequest('/p/sub3', {detached:true});
r.subrequest('/p/sub4', 'a=1&b=2').then(reply => r.return(reply.status,
- JSON.stringify(JSON.parse(reply.responseBody))));
+ JSON.stringify(JSON.parse(reply.responseBody ?? ''))));
}
diff --git a/test/ts/tsconfig.json b/test/ts/tsconfig.json
new file mode 100644
index 000000000..f11f689a5
--- /dev/null
+++ b/test/ts/tsconfig.json
@@ -0,0 +1,35 @@
+{
+ "compilerOptions": {
+ "target": "ES5",
+ "module": "es2015",
+ "lib": [
+ "ES2015",
+ "ES2016.Array.Include",
+ "ES2017.Object",
+ "ES2017.String"
+ ],
+ "noEmit": true,
+ "downlevelIteration": true,
+
+ "strict": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+ "strictFunctionTypes": true,
+ "strictBindCallApply": true,
+ "strictPropertyInitialization": true,
+ "noImplicitThis": true,
+ "alwaysStrict": true,
+
+ "moduleResolution": "node",
+ "forceConsistentCasingInFileNames": true
+ },
+ "include": [
+ "**/*.ts"
+ ],
+ "exclude": [
+ "./node_modules/**"
+ ],
+ "files": [
+ "./node_modules/njs-types/ngx_http_js_module.d.ts"
+ ]
+}
diff --git a/ts/README.md b/ts/README.md
new file mode 100644
index 000000000..de28f6d6b
--- /dev/null
+++ b/ts/README.md
@@ -0,0 +1,79 @@
+# TypeScript definitions for njs
+
+This package contains type definitions for [njs](https://github.com/nginx/njs) – NGINX JavaScript.
+
+
+## Usage
+
+Install **njs-types** from the [npm registry](https://www.npmjs.com/) into your project:
+
+```sh
+# using npm:
+npm install --save-dev njs-types
+# or using yarn:
+yarn add --dev njs-types
+```
+
+njs-types provides three entry points with global declarations for each of njs environments:
+
+* `njs_shell.d.ts` – njs shell
+* `ngx_http_js_module.d.ts` – NGINX JS HTTP Module
+* `ngx_stream_js_module.d.ts` – NGINX JS Stream Module
+
+You can either reference them using [triple-slash directive](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html) at top of your `.ts` files (adjust `path` to point into your project’s node_modules):
+
+```ts
+///
+```
+
+or include them using the [files](https://www.typescriptlang.org/tsconfig#files) flag in your `tsconfig.json`, for example:
+
+```json
+{
+ "compilerOptions": {
+ "target": "ES5",
+ "module": "es2015",
+ "lib": [
+ "ES2015",
+ "ES2016.Array.Include",
+ "ES2017.Object",
+ "ES2017.String",
+ ],
+ "outDir": "./lib",
+ "downlevelIteration": true,
+
+ "strict": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+ "strictFunctionTypes": true,
+ "strictBindCallApply": true,
+ "strictPropertyInitialization": true,
+ "noImplicitThis": true,
+ "alwaysStrict": true,
+
+ "moduleResolution": "node",
+
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ },
+ "include": [
+ "./src",
+ ],
+ "files": [
+ "./node_modules/njs-types/ngx_http_js_module.d.ts",
+ ],
+}
+```
+
+
+## Versions
+
+njs-types is typically being released together with njs.
+Their major and minor release numbers (the first two numbers) are always aligned, but the patch version (the third number) may differ.
+That's because njs-types may be updated between njs releases and in such case the patch version is incremented.
+
+It's the same strategy as used in [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped#how-do-definitely-typed-package-versions-relate-to-versions-of-the-corresponding-library).
+The reason is that npmjs enforces [SemVer](https://semver.org/) which doesn't allow four-part version number nor provide post-release suffixes.
+
+You can find from which commit the package was built in file `COMMITHASH` inside the published package.
+It contains global revision id in the upstream repository https://hg.nginx.org/njs/ (Mercurial).
diff --git a/ts/index.d.ts b/ts/index.d.ts
new file mode 100644
index 000000000..8a324cc6c
--- /dev/null
+++ b/ts/index.d.ts
@@ -0,0 +1,4 @@
+///
+///
+///
+///
diff --git a/nginx/ts/ngx_http_js_module.d.ts b/ts/ngx_http_js_module.d.ts
similarity index 98%
rename from nginx/ts/ngx_http_js_module.d.ts
rename to ts/ngx_http_js_module.d.ts
index d29b1385c..c6b94e4f6 100644
--- a/nginx/ts/ngx_http_js_module.d.ts
+++ b/ts/ngx_http_js_module.d.ts
@@ -1,4 +1,4 @@
-///
+///
interface NginxHTTPArgs {
readonly [prop: string]: NjsByteString;
@@ -39,7 +39,7 @@ interface NginxHeadersIn {
readonly 'Warning'?: NjsByteString;
readonly 'X-Forwarded-For'?: NjsByteString;
- readonly [prop: string]: NjsByteString;
+ readonly [prop: string]: NjsByteString | undefined;
}
interface NginxHeadersOut {
@@ -76,7 +76,7 @@ interface NginxHeadersOut {
'Set-Cookie'?: NjsStringLike[];
- [prop: string]: NjsStringLike | NjsStringLike[];
+ [prop: string]: NjsStringLike | NjsStringLike[] | undefined;
}
interface NginxVariables {
@@ -229,7 +229,7 @@ interface NginxVariables {
readonly 'upstream_trailer_'?: NjsByteString;
readonly 'uri'?: NjsByteString;
- [prop: string]: NjsStringLike;
+ [prop: string]: NjsStringLike | undefined;
}
interface NginxSubrequestOptions {
diff --git a/nginx/ts/ngx_stream_js_module.d.ts b/ts/ngx_stream_js_module.d.ts
similarity index 98%
rename from nginx/ts/ngx_stream_js_module.d.ts
rename to ts/ngx_stream_js_module.d.ts
index 47799ba23..46aa76dd6 100644
--- a/nginx/ts/ngx_stream_js_module.d.ts
+++ b/ts/ngx_stream_js_module.d.ts
@@ -1,4 +1,4 @@
-///
+///
interface NginxStreamVariables {
readonly 'binary_remote_addr'?: NjsByteString;
@@ -66,7 +66,7 @@ interface NginxStreamVariables {
readonly 'time_iso8601'?: NjsByteString;
readonly 'time_local'?: NjsByteString;
- [prop: string]: NjsByteString;
+ [prop: string]: NjsByteString | undefined;
}
interface NginxStreamCallbackFlags {
diff --git a/src/ts/njs_core.d.ts b/ts/njs_core.d.ts
similarity index 100%
rename from src/ts/njs_core.d.ts
rename to ts/njs_core.d.ts
diff --git a/src/ts/crypto.d.ts b/ts/njs_modules/crypto.d.ts
similarity index 98%
rename from src/ts/crypto.d.ts
rename to ts/njs_modules/crypto.d.ts
index 5895b3e74..345a2d1a7 100644
--- a/src/ts/crypto.d.ts
+++ b/ts/njs_modules/crypto.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare module "crypto" {
diff --git a/src/ts/fs.d.ts b/ts/njs_modules/fs.d.ts
similarity index 99%
rename from src/ts/fs.d.ts
rename to ts/njs_modules/fs.d.ts
index 682cee490..cc24f243a 100644
--- a/src/ts/fs.d.ts
+++ b/ts/njs_modules/fs.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare module "fs" {
diff --git a/src/ts/querystring.d.ts b/ts/njs_modules/querystring.d.ts
similarity index 99%
rename from src/ts/querystring.d.ts
rename to ts/njs_modules/querystring.d.ts
index 0368ec1c4..4058f5a1c 100644
--- a/src/ts/querystring.d.ts
+++ b/ts/njs_modules/querystring.d.ts
@@ -1,4 +1,4 @@
-///
+///
declare module "querystring" {
diff --git a/src/ts/njs_shell.d.ts b/ts/njs_shell.d.ts
similarity index 70%
rename from src/ts/njs_shell.d.ts
rename to ts/njs_shell.d.ts
index 3a6aa5ed6..e81802584 100644
--- a/src/ts/njs_shell.d.ts
+++ b/ts/njs_shell.d.ts
@@ -1,4 +1,4 @@
-///
+///
interface Console {
log(...args: any[]): void;
@@ -7,3 +7,5 @@ interface Console {
time(label?: NjsStringLike): void;
timeEnd(label?: NjsStringLike): void;
}
+
+declare const console: Console;
diff --git a/ts/package.json b/ts/package.json
new file mode 100644
index 000000000..e7b2eb779
--- /dev/null
+++ b/ts/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "njs-types",
+ "version": "0.0.0-dev",
+ "description": "TypeScript definitions for njs scripting language and nginx js modules.",
+ "scripts": {
+ "lint": "tsc"
+ },
+ "files": [
+ "**/*.d.ts",
+ "COMMITHASH"
+ ],
+ "types": "index.d.ts",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/nginx/njs.git"
+ },
+ "keywords": [
+ "nginx",
+ "njs",
+ "types"
+ ],
+ "author": "NGINX, Inc.",
+ "license": "BSD-2-Clause",
+ "bugs": {
+ "url": "https://github.com/nginx/njs/issues"
+ },
+ "homepage": "https://nginx.org/en/docs/njs/",
+ "devDependencies": {
+ "typescript": "^4.0.3"
+ }
+}
diff --git a/ts/tsconfig.json b/ts/tsconfig.json
new file mode 100644
index 000000000..b9a969a9a
--- /dev/null
+++ b/ts/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES5",
+ "module": "es2015",
+ "lib": [
+ "ES2015",
+ "ES2016.Array.Include",
+ "ES2017.Object",
+ "ES2017.String"
+ ],
+ "noEmit": true,
+ "downlevelIteration": true,
+ "strict": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+ "strictFunctionTypes": true,
+ "strictBindCallApply": true,
+ "strictPropertyInitialization": true,
+ "noImplicitThis": true,
+ "alwaysStrict": true,
+
+ "moduleResolution": "node",
+ "forceConsistentCasingInFileNames": true
+ }
+}