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 + } +}