├── .clang-format ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── codeql.yml │ ├── dependency-review.yml │ ├── linter.yml │ ├── nodejs.yml │ ├── scorecards.yml │ ├── test-website.yml │ └── website.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── original_docs_source.md ├── package.json ├── src ├── 1-getting-started │ ├── 1_hello_world │ │ ├── README.md │ │ ├── nan │ │ │ ├── binding.gyp │ │ │ ├── hello.cc │ │ │ ├── hello.js │ │ │ └── package.json │ │ ├── napi │ │ │ ├── binding.gyp │ │ │ ├── hello.c │ │ │ ├── hello.js │ │ │ └── package.json │ │ ├── node-addon-api-addon-class │ │ │ ├── binding.gyp │ │ │ ├── hello.cc │ │ │ ├── hello.js │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── binding.gyp │ │ │ ├── hello.cc │ │ │ ├── hello.js │ │ │ └── package.json │ ├── 2_function_arguments │ │ ├── README.md │ │ ├── nan │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ ├── napi │ │ │ ├── addon.c │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ ├── 3_callbacks │ │ ├── nan │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ ├── napi │ │ │ ├── addon.c │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ ├── 4_object_factory │ │ ├── nan │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ ├── napi │ │ │ ├── addon.c │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ ├── 5_function_factory │ │ ├── nan │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ ├── napi │ │ │ ├── addon.c │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ ├── 6_object_wrap │ │ ├── nan │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ │ ├── napi │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ ├── 7_factory_wrap │ │ ├── nan │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ │ ├── napi │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ └── a-first-project │ │ └── node-addon-api │ │ ├── README.md │ │ ├── binding.gyp │ │ ├── lib │ │ └── binding.js │ │ ├── package.json │ │ ├── src │ │ └── hello_world.cc │ │ └── test │ │ └── test_binding.js ├── 2-js-to-native-conversion │ ├── 8_passing_wrapped │ │ ├── nan │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ │ ├── napi │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ ├── myobject.cc │ │ │ ├── myobject.h │ │ │ └── package.json │ ├── array_buffer_to_native │ │ └── node-addon-api │ │ │ ├── array_buffer_to_native.cc │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ └── package.json │ ├── object-template-demo │ │ ├── README.md │ │ ├── nan │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ ├── object-template-demo.cc │ │ │ └── package.json │ │ └── napi │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ ├── node-api-common.h │ │ │ ├── object-template-demo.cc │ │ │ ├── package.json │ │ │ ├── proxy-template.cc │ │ │ └── proxy-template.h │ ├── object-wrap-demo │ │ └── node-addon-api │ │ │ ├── README.md │ │ │ ├── binding.gyp │ │ │ ├── lib │ │ │ └── binding.js │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── object_wrap_demo.cc │ │ │ └── object_wrap_demo.h │ │ │ └── test │ │ │ └── test_binding.js │ └── typed_array_to_native │ │ └── node-addon-api │ │ ├── binding.gyp │ │ ├── index.js │ │ ├── package.json │ │ └── typed_array_to_native.cc ├── 3-context-awareness │ ├── napi │ │ ├── binding.gyp │ │ ├── index.js │ │ ├── multiple_load.c │ │ └── package.json │ └── node_10 │ │ ├── binding.gyp │ │ ├── index.js │ │ ├── multiple_load.cc │ │ └── package.json ├── 4-references-and-handle-scope │ └── function-reference-demo │ │ └── node-addon-api │ │ ├── binding.gyp │ │ ├── index.js │ │ ├── package.json │ │ └── src │ │ ├── binding.cc │ │ ├── native-addon.cc │ │ └── native-addon.h ├── 5-async-work │ ├── async-iterator │ │ └── node-addon-api │ │ │ ├── CMakeLists.txt │ │ │ ├── example.cc │ │ │ ├── index.js │ │ │ └── package.json │ ├── async_pi_estimate │ │ ├── nan │ │ │ ├── README.md │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── async.cc │ │ │ ├── async.h │ │ │ ├── binding.gyp │ │ │ ├── package.json │ │ │ ├── pi_est.cc │ │ │ ├── pi_est.h │ │ │ ├── sync.cc │ │ │ └── sync.h │ │ └── node-addon-api │ │ │ ├── README.md │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── async.cc │ │ │ ├── async.h │ │ │ ├── binding.gyp │ │ │ ├── package.json │ │ │ ├── pi_est.cc │ │ │ ├── pi_est.h │ │ │ ├── sync.cc │ │ │ └── sync.h │ ├── async_work_promise │ │ ├── napi │ │ │ ├── binding.c │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ └── package.json │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── worker.h │ ├── async_work_thread_safe_function │ │ └── napi │ │ │ ├── binding.c │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ └── package.json │ ├── call-js-from-async-worker-execute │ │ └── node-addon-api │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── src │ │ │ └── binding.cc │ └── napi-asyncworker-example │ │ └── node-addon-api │ │ ├── README.md │ │ ├── binding.gyp │ │ ├── package.json │ │ ├── src │ │ ├── RunSimpleAsyncWorker.cc │ │ ├── SimpleAsyncWorker.cc │ │ └── SimpleAsyncWorker.h │ │ └── test │ │ └── Test.js ├── 6-threadsafe-function │ ├── promise-callback-demo │ │ └── node-addon-api │ │ │ ├── README.md │ │ │ ├── binding.gyp │ │ │ ├── package.json │ │ │ ├── src │ │ │ └── promise_callback_demo.cc │ │ │ └── test │ │ │ └── index.js │ ├── thread_safe_function_counting │ │ └── node-addon-api │ │ │ ├── addon.cc │ │ │ ├── addon.js │ │ │ ├── binding.gyp │ │ │ └── package.json │ ├── thread_safe_function_round_trip │ │ └── napi │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── round_trip.c │ ├── thread_safe_function_with_object_wrap │ │ └── node-addon-api │ │ │ ├── binding.gyp │ │ │ ├── package.json │ │ │ ├── tsfn_object_wrap.cc │ │ │ └── tsfn_object_wrap.js │ ├── threadsafe-async-iterator │ │ └── node-addon-api │ │ │ ├── CMakeLists.txt │ │ │ ├── example.cc │ │ │ ├── index.js │ │ │ └── package.json │ └── typed_threadsafe_function │ │ └── node-addon-api │ │ ├── CMakeLists.txt │ │ ├── clock.cc │ │ ├── index.js │ │ └── package.json ├── 7-events │ ├── emit_event_from_cpp │ │ └── node-addon-api │ │ │ ├── binding.gyp │ │ │ ├── index.js │ │ │ ├── package.json │ │ │ └── src │ │ │ └── emit-from-cpp.cc │ └── inherits_from_event_emitter │ │ └── node-addon-api │ │ ├── binding.gyp │ │ ├── index.js │ │ ├── package.json │ │ └── src │ │ ├── binding.cc │ │ ├── native-emitter.cc │ │ └── native-emitter.h └── 8-tooling │ ├── build_with_cmake │ ├── README.md │ ├── napi │ │ ├── CMakeLists.txt │ │ ├── hello.c │ │ ├── hello.js │ │ └── package.json │ └── node-addon-api │ │ ├── CMakeLists.txt │ │ ├── hello.cc │ │ ├── hello.js │ │ └── package.json │ └── typescript_with_addon │ └── node-addon-api │ ├── CMakeLists.txt │ ├── README.md │ ├── cPart.c │ ├── cPart.h │ ├── cppPart.cpp │ ├── index.ts │ ├── jsPart.js │ ├── napiPart.cpp │ ├── package.json │ └── tsconfig.json ├── test_all.js └── website ├── .editorconfig ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── .gitkeep ├── about │ ├── uses.md │ └── what.md ├── build-tools │ ├── cmake-js.md │ ├── node-gyp.md │ ├── node-pre-gyp.md │ └── prebuild.md ├── getting-started │ ├── first.md │ ├── helloworld.md │ ├── migration.md │ ├── objectwrap.md │ ├── prerequisites.md │ └── tools.md ├── index.md ├── menu.json ├── special-topics │ ├── asyncworker.md │ ├── context-awareness.md │ ├── object-function-refs.md │ └── thread-safe-functions.md └── toc.json ├── gatsby-config.js ├── gatsby-node.js ├── package.json ├── src ├── components │ ├── docs │ │ ├── DocsHeader │ │ │ ├── DocsHeader.tsx │ │ │ └── index.ts │ │ ├── DocsWrapper │ │ │ ├── DocsWrapper.tsx │ │ │ └── index.ts │ │ └── TableOfContents │ │ │ ├── TocFloatingButton.tsx │ │ │ ├── TocWrapper.tsx │ │ │ └── index.ts │ ├── foundations │ │ ├── Theme.tsx │ │ ├── box │ │ │ ├── components │ │ │ │ ├── BorderBox.tsx │ │ │ │ ├── Box.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── reset │ │ │ ├── components │ │ │ │ ├── GlobalStyles.ts │ │ │ │ └── ThemeReset.tsx │ │ │ ├── index.ts │ │ │ └── styles │ │ │ │ ├── base.ts │ │ │ │ ├── code.ts │ │ │ │ └── reboot.ts │ │ ├── typography │ │ │ ├── components │ │ │ │ ├── Heading.tsx │ │ │ │ ├── Link.tsx │ │ │ │ ├── Paragraph.tsx │ │ │ │ ├── Text.tsx │ │ │ │ └── Typography.tsx │ │ │ ├── index.ts │ │ │ └── utils │ │ │ │ ├── determineFontDimensions.tsx │ │ │ │ └── index.ts │ │ └── variables.ts │ ├── layout │ │ ├── Container │ │ │ ├── Container.tsx │ │ │ └── index.ts │ │ ├── Footer │ │ │ ├── Footer.tsx │ │ │ ├── FooterWrapper.tsx │ │ │ └── index.ts │ │ ├── Header │ │ │ ├── Header.tsx │ │ │ ├── HeaderInner.tsx │ │ │ └── index.ts │ │ ├── LayoutMain │ │ │ ├── LayoutMain.tsx │ │ │ └── index.tsx │ │ ├── LayoutRoot │ │ │ ├── LayoutRoot.tsx │ │ │ └── index.tsx │ │ ├── Navigation │ │ │ ├── NavButton.tsx │ │ │ ├── Navigation.tsx │ │ │ ├── NavigationContext.tsx │ │ │ ├── NavigationMenu.tsx │ │ │ └── index.ts │ │ ├── Overlay │ │ │ ├── Overlay.tsx │ │ │ └── index.ts │ │ └── Page │ │ │ ├── NotFoundWrapper.tsx │ │ │ ├── Page.tsx │ │ │ └── index.ts │ ├── page │ │ └── Markdown │ │ │ ├── MarkdownComponents.tsx │ │ │ ├── MarkdownContent.tsx │ │ │ └── index.ts │ └── ui │ │ └── Pagination │ │ ├── Pagination.tsx │ │ └── index.ts ├── interfaces │ ├── gatsby.ts │ └── nodes.ts ├── layouts │ └── index.tsx ├── pages │ └── 404.tsx ├── templates │ ├── home.tsx │ └── page.tsx ├── typings.d.ts └── utils │ ├── helpers.ts │ ├── renderAst.tsx │ └── types.ts └── static └── img ├── homepage.png ├── new-repo-from-template.png └── use-template.png /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 31 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | groups: 8 | all: 9 | patterns: 10 | - '*' 11 | 12 | - package-ecosystem: npm 13 | directory: / 14 | schedule: 15 | interval: weekly 16 | 17 | - package-ecosystem: npm 18 | directories: 19 | - /src/**/* 20 | schedule: 21 | interval: weekly 22 | groups: 23 | all: 24 | patterns: 25 | - '*' 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 21 | with: 22 | egress-policy: audit 23 | 24 | - name: 'Checkout Repository' 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: 'Dependency Review' 27 | uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0 28 | -------------------------------------------------------------------------------- /.github/workflows/linter.yml: -------------------------------------------------------------------------------- 1 | name: clang-format 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | check-clang-format: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Harden Runner 13 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 14 | with: 15 | egress-policy: audit 16 | 17 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 18 | - run: npm install 19 | - name: check clang-format 20 | run: | 21 | git config clangFormat.binary node_modules/.bin/clang-format 22 | git config clangFormat.style file 23 | npx check-clang-format 24 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: "0 0 * * *" 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | test: 14 | runs-on: ${{ matrix.operating-system }} 15 | 16 | strategy: 17 | matrix: 18 | node-version: [16.x, 18.x, 20.x] 19 | operating-system: [ubuntu-latest, windows-2019, macos-latest] 20 | 21 | steps: 22 | - name: Harden Runner 23 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 24 | with: 25 | egress-policy: audit 26 | 27 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 28 | - name: Use Node.js ${{ matrix.node-version }} 29 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | - run: npm install -g cmake-js@6.3.2 33 | - run: npm install 34 | - name: Environment Information 35 | run: npx envinfo 36 | - name: Run Test 37 | run: npm test 38 | 39 | nightly-daily-test: 40 | runs-on: ubuntu-latest 41 | container: node 42 | steps: 43 | - name: Harden Runner 44 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 45 | with: 46 | egress-policy: audit 47 | 48 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 49 | - run: npm i -g n && n nightly 50 | - run: node -p process.versions 51 | - name: Environment Information 52 | run: npx envinfo 53 | - run: npm install -g cmake-js@6.3.2 54 | - run: npm install 55 | - name: Environment Information 56 | run: npx envinfo 57 | - name: Run Test 58 | continue-on-error: true 59 | run: npm test 60 | -------------------------------------------------------------------------------- /.github/workflows/test-website.yml: -------------------------------------------------------------------------------- 1 | name: Test Website Builds 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | test-website: 10 | runs-on: ubuntu-latest 11 | container: node 12 | steps: 13 | - name: Harden Runner 14 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 15 | with: 16 | egress-policy: audit 17 | 18 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | - run: npx envinfo 20 | - name: Build and Deploy 21 | env: 22 | NODE_OPTIONS: --openssl-legacy-provider 23 | working-directory: ./website 24 | run: | 25 | npm install 26 | npx gatsby build --prefix-paths 27 | -------------------------------------------------------------------------------- /.github/workflows/website.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy the Website 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | permissions: 8 | contents: write # Pushes to `gh-pages` branch 9 | 10 | jobs: 11 | deploy-website: 12 | runs-on: ubuntu-latest 13 | container: node 14 | steps: 15 | - name: Harden Runner 16 | uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 17 | with: 18 | egress-policy: audit 19 | 20 | - name: Checkout 21 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 22 | with: 23 | persist-credentials: false 24 | - run: npx envinfo 25 | - name: Build and Deploy 26 | env: 27 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | REPO: ${{ github.repository }} 29 | USER: gatsby-deploy-bot 30 | NODE_OPTIONS: --openssl-legacy-provider 31 | working-directory: ./website 32 | run: | 33 | npm install 34 | npx gatsby build --prefix-paths 35 | npx gh-pages -d public -r https://git:${TOKEN}@github.com/${REPO}.git -u "${USER}" 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | Debug/ 4 | Release/ 5 | *.lock 6 | *.log 7 | package-lock.json 8 | npm-debug.log* 9 | 10 | .idea/ 11 | .vscode/ 12 | .DS_Store 13 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/gitleaks/gitleaks 3 | rev: v8.16.3 4 | hooks: 5 | - id: gitleaks 6 | - repo: https://github.com/pocc/pre-commit-hooks 7 | rev: v1.3.5 8 | hooks: 9 | - id: cpplint 10 | - repo: https://github.com/pre-commit/mirrors-eslint 11 | rev: v8.38.0 12 | hooks: 13 | - id: eslint 14 | - repo: https://github.com/pre-commit/pre-commit-hooks 15 | rev: v4.4.0 16 | hooks: 17 | - id: end-of-file-fixer 18 | - id: trailing-whitespace 19 | - repo: https://github.com/pylint-dev/pylint 20 | rev: v2.17.2 21 | hooks: 22 | - id: pylint 23 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The Node.js Code of Conduct, which applies to this project, can be found at 4 | https://github.com/nodejs/admin/blob/HEAD/CODE_OF_CONDUCT.md. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Developer's Certificate of Origin 1.1 3 | 4 | By making a contribution to this project, I certify that: 5 | 6 | (a) The contribution was created in whole or in part by me and I 7 | have the right to submit it under the open-source license 8 | indicated in the file; or 9 | 10 | (b) The contribution is based upon previous work that, to the best 11 | of my knowledge, is covered under an appropriate open source 12 | license and I have the right under that license to submit that 13 | work with modifications, whether created in whole or in part 14 | by me, under the same open-source license (unless I am 15 | permitted to submit under a different license), as indicated 16 | in the file; or 17 | 18 | (c) The contribution was provided directly to me by some other 19 | person who certified (a), (b), or (c) and I have not modified 20 | it. 21 | 22 | (d) I understand and agree that this project and the contribution 23 | are public and that a record of the contribution (including all 24 | personal information I submit with it, including my sign-off) is 25 | maintained indefinitely and may be redistributed consistent with 26 | this project or the open source license(s) involved. 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2017 Node.js node-addon-examples collaborators 5 | ----------------------------------- 6 | 7 | *Node.js node-addon-examples collaborators listed at * 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-addon-examples", 3 | "version": "1.0.0", 4 | "description": "Node.js Addon Examples", 5 | "main": "test_all.js", 6 | "scripts": { 7 | "format": "clang-format -i --glob=*/**/*.{h,cpp,cc}", 8 | "test": "node test_all.js" 9 | }, 10 | "husky": { 11 | "hooks": { 12 | "pre-commit": "lint-staged" 13 | } 14 | }, 15 | "lint-staged": { 16 | "*.{h,cpp,cc}": [ 17 | "npm run format", 18 | "git add" 19 | ] 20 | }, 21 | "dependencies": { 22 | "chalk": "^5.4.1", 23 | "clang-format": "^1.4.0", 24 | "cmake-js": "^7.1.1", 25 | "semver": "^7.1.3" 26 | }, 27 | "devDependencies": { 28 | "husky": "^4.3.0", 29 | "lint-staged": "^15.4.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/README.md: -------------------------------------------------------------------------------- 1 | ## Example 1: *Hello world* 2 | 3 | To get started let's make a small addon which is the C++ equivalent of 4 | the following JavaScript code: 5 | 6 | ```js 7 | module.exports.hello = function() { return 'world'; }; 8 | ``` 9 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "hello", 5 | "sources": [ "hello.cc" ], 6 | "include_dirs": [ 7 | " 2 | 3 | void Method(const Nan::FunctionCallbackInfo& info) { 4 | info.GetReturnValue().Set(Nan::New("world").ToLocalChecked()); 5 | } 6 | 7 | void Init(v8::Local exports) { 8 | v8::Local context = 9 | exports->GetCreationContext().ToLocalChecked(); 10 | exports->Set(context, 11 | Nan::New("hello").ToLocalChecked(), 12 | Nan::New(Method) 13 | ->GetFunction(context) 14 | .ToLocalChecked()); 15 | } 16 | 17 | NODE_MODULE(hello, Init) 18 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/nan/hello.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('hello'); 2 | 3 | console.log(addon.hello()); // 'world' -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/nan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #1", 5 | "main": "hello.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0", 9 | "nan": "^2.0.0" 10 | }, 11 | "scripts": { 12 | "test": "node hello.js" 13 | }, 14 | "gypfile": true 15 | } 16 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "hello", 5 | "sources": [ "hello.c" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/napi/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static napi_value Method(napi_env env, napi_callback_info info) { 5 | napi_status status; 6 | napi_value world; 7 | status = napi_create_string_utf8(env, "world", 5, &world); 8 | assert(status == napi_ok); 9 | return world; 10 | } 11 | 12 | #define DECLARE_NAPI_METHOD(name, func) \ 13 | { name, 0, func, 0, 0, 0, napi_default, 0 } 14 | 15 | static napi_value Init(napi_env env, napi_value exports) { 16 | napi_status status; 17 | napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method); 18 | status = napi_define_properties(env, exports, 1, &desc); 19 | assert(status == napi_ok); 20 | return exports; 21 | } 22 | 23 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 24 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/napi/hello.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('hello'); 2 | 3 | console.log(addon.hello()); // 'world' 4 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #1", 5 | "main": "hello.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0" 9 | }, 10 | "scripts": { 11 | "test": "node hello.js" 12 | }, 13 | "gypfile": true 14 | } 15 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/node-addon-api-addon-class/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "hello", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "hello.cc" ], 8 | "include_dirs": [ 9 | " 2 | 3 | class HelloAddon : public Napi::Addon { 4 | public: 5 | HelloAddon(Napi::Env env, Napi::Object exports) { 6 | DefineAddon(exports, 7 | {InstanceMethod("hello", &HelloAddon::Hello, napi_enumerable)}); 8 | } 9 | 10 | private: 11 | Napi::Value Hello(const Napi::CallbackInfo& info) { 12 | return Napi::String::New(info.Env(), "world"); 13 | } 14 | }; 15 | 16 | NODE_API_ADDON(HelloAddon) 17 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/node-addon-api-addon-class/hello.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('hello'); 2 | 3 | console.log(addon.hello()); // 'world' 4 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/node-addon-api-addon-class/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #1", 5 | "main": "hello.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0", 9 | "node-addon-api": "^8.1.0" 10 | }, 11 | "scripts": { 12 | "test": "node hello.js" 13 | }, 14 | "gypfile": true 15 | } 16 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "hello", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "hello.cc" ], 8 | "include_dirs": [ 9 | " 2 | 3 | Napi::String Method(const Napi::CallbackInfo& info) { 4 | Napi::Env env = info.Env(); 5 | return Napi::String::New(env, "world"); 6 | } 7 | 8 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 9 | exports.Set(Napi::String::New(env, "hello"), 10 | Napi::Function::New(env, Method)); 11 | return exports; 12 | } 13 | 14 | NODE_API_MODULE(hello, Init) 15 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/node-addon-api/hello.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('hello'); 2 | 3 | console.log(addon.hello()); // 'world' 4 | -------------------------------------------------------------------------------- /src/1-getting-started/1_hello_world/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello_world", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #1", 5 | "main": "hello.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0", 9 | "node-addon-api": "^8.1.0" 10 | }, 11 | "scripts": { 12 | "test": "node hello.js" 13 | }, 14 | "gypfile": true 15 | } 16 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/README.md: -------------------------------------------------------------------------------- 1 | ## Example 2: *Function arguments* 2 | 3 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/nan/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void Add(const Nan::FunctionCallbackInfo& info) { 4 | v8::Local context = info.GetIsolate()->GetCurrentContext(); 5 | 6 | if (info.Length() < 2) { 7 | Nan::ThrowTypeError("Wrong number of arguments"); 8 | return; 9 | } 10 | 11 | if (!info[0]->IsNumber() || !info[1]->IsNumber()) { 12 | Nan::ThrowTypeError("Wrong arguments"); 13 | return; 14 | } 15 | 16 | double arg0 = info[0]->NumberValue(context).FromJust(); 17 | double arg1 = info[1]->NumberValue(context).FromJust(); 18 | v8::Local num = Nan::New(arg0 + arg1); 19 | 20 | info.GetReturnValue().Set(num); 21 | } 22 | 23 | void Init(v8::Local exports) { 24 | v8::Local context = 25 | exports->GetCreationContext().ToLocalChecked(); 26 | exports->Set(context, 27 | Nan::New("add").ToLocalChecked(), 28 | Nan::New(Add) 29 | ->GetFunction(context) 30 | .ToLocalChecked()); 31 | } 32 | 33 | NODE_MODULE(addon, Init) 34 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/nan/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon.node') 2 | 3 | console.log('This should be eight:', addon.add(3, 5)) -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc" ], 6 | "include_dirs": [ 7 | " 2 | #include 3 | #include 4 | static napi_value Add(napi_env env, napi_callback_info info) { 5 | napi_status status; 6 | 7 | size_t argc = 2; 8 | napi_value args[2]; 9 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 10 | assert(status == napi_ok); 11 | 12 | if (argc < 2) { 13 | napi_throw_type_error(env, NULL, "Wrong number of arguments"); 14 | return NULL; 15 | } 16 | 17 | napi_valuetype valuetype0; 18 | status = napi_typeof(env, args[0], &valuetype0); 19 | assert(status == napi_ok); 20 | 21 | napi_valuetype valuetype1; 22 | status = napi_typeof(env, args[1], &valuetype1); 23 | assert(status == napi_ok); 24 | 25 | if (valuetype0 != napi_number || valuetype1 != napi_number) { 26 | napi_throw_type_error(env, NULL, "Wrong arguments"); 27 | return NULL; 28 | } 29 | 30 | double value0; 31 | status = napi_get_value_double(env, args[0], &value0); 32 | assert(status == napi_ok); 33 | 34 | double value1; 35 | status = napi_get_value_double(env, args[1], &value1); 36 | assert(status == napi_ok); 37 | 38 | napi_value sum; 39 | status = napi_create_double(env, value0 + value1, &sum); 40 | assert(status == napi_ok); 41 | 42 | return sum; 43 | } 44 | 45 | #define DECLARE_NAPI_METHOD(name, func) \ 46 | { name, 0, func, 0, 0, 0, napi_default, 0 } 47 | 48 | napi_value Init(napi_env env, napi_value exports) { 49 | napi_status status; 50 | napi_property_descriptor addDescriptor = DECLARE_NAPI_METHOD("add", Add); 51 | status = napi_define_properties(env, exports, 1, &addDescriptor); 52 | assert(status == napi_ok); 53 | return exports; 54 | } 55 | 56 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 57 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/napi/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon.node') 2 | 3 | console.log('This should be eight:', addon.add(3, 5)) -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.c" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "function_arguments", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #2", 5 | "main": "addon.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0" 9 | }, 10 | "scripts": { 11 | "test": "node addon.js" 12 | }, 13 | "gypfile": true 14 | } 15 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Napi::Value Add(const Napi::CallbackInfo& info) { 4 | Napi::Env env = info.Env(); 5 | 6 | if (info.Length() < 2) { 7 | Napi::TypeError::New(env, "Wrong number of arguments") 8 | .ThrowAsJavaScriptException(); 9 | return env.Null(); 10 | } 11 | 12 | if (!info[0].IsNumber() || !info[1].IsNumber()) { 13 | Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); 14 | return env.Null(); 15 | } 16 | 17 | double arg0 = info[0].As().DoubleValue(); 18 | double arg1 = info[1].As().DoubleValue(); 19 | Napi::Number num = Napi::Number::New(env, arg0 + arg1); 20 | 21 | return num; 22 | } 23 | 24 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 25 | exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add)); 26 | return exports; 27 | } 28 | 29 | NODE_API_MODULE(addon, Init) 30 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon.node') 2 | 3 | console.log('This should be eight:', addon.add(3, 5)) 4 | -------------------------------------------------------------------------------- /src/1-getting-started/2_function_arguments/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "addon.cc" ], 8 | "include_dirs": [ 9 | " 2 | 3 | void RunCallback(const Nan::FunctionCallbackInfo& info) { 4 | v8::Local cb = info[0].As(); 5 | const unsigned argc = 1; 6 | v8::Local argv[argc] = {Nan::New("hello world").ToLocalChecked()}; 7 | Nan::AsyncResource resource("nan:makeCallback"); 8 | resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, argc, argv); 9 | } 10 | 11 | void Init(v8::Local exports, v8::Local module) { 12 | Nan::SetMethod(module, "exports", RunCallback); 13 | } 14 | 15 | NODE_MODULE(addon, Init) 16 | -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/nan/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | addon(function(msg){ 4 | console.log(msg); // 'hello world' 5 | }); -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc" ], 6 | "include_dirs": [ 7 | " 2 | #include 3 | 4 | static napi_value RunCallback(napi_env env, const napi_callback_info info) { 5 | napi_status status; 6 | 7 | size_t argc = 1; 8 | napi_value args[1]; 9 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 10 | assert(status == napi_ok); 11 | 12 | napi_value cb = args[0]; 13 | 14 | napi_value argv[1]; 15 | status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, argv); 16 | assert(status == napi_ok); 17 | 18 | napi_value global; 19 | status = napi_get_global(env, &global); 20 | assert(status == napi_ok); 21 | 22 | napi_value result; 23 | status = napi_call_function(env, global, cb, 1, argv, &result); 24 | assert(status == napi_ok); 25 | 26 | return NULL; 27 | } 28 | 29 | static napi_value Init(napi_env env, napi_value exports) { 30 | napi_value new_exports; 31 | napi_status status = napi_create_function( 32 | env, "", NAPI_AUTO_LENGTH, RunCallback, NULL, &new_exports); 33 | assert(status == napi_ok); 34 | return new_exports; 35 | } 36 | 37 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 38 | -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/napi/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | addon(function(msg){ 4 | console.log(msg); // 'hello world' 5 | }); 6 | -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.c" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "napi_callbacks", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #3", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void RunCallback(const Napi::CallbackInfo& info) { 4 | Napi::Env env = info.Env(); 5 | Napi::Function cb = info[0].As(); 6 | cb.Call(env.Global(), {Napi::String::New(env, "hello world")}); 7 | } 8 | 9 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 10 | return Napi::Function::New(env, RunCallback); 11 | } 12 | 13 | NODE_API_MODULE(addon, Init) 14 | -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | addon(function(msg){ 4 | console.log(msg); // 'hello world' 5 | }); 6 | -------------------------------------------------------------------------------- /src/1-getting-started/3_callbacks/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "addon.cc" ], 8 | "include_dirs": [ 9 | " 2 | 3 | void CreateObject(const Nan::FunctionCallbackInfo& info) { 4 | v8::Local context = info.GetIsolate()->GetCurrentContext(); 5 | v8::Local obj = Nan::New(); 6 | obj->Set(context, 7 | Nan::New("msg").ToLocalChecked(), 8 | info[0]->ToString(context).ToLocalChecked()); 9 | 10 | info.GetReturnValue().Set(obj); 11 | } 12 | 13 | void Init(v8::Local exports, v8::Local module) { 14 | v8::Local context = 15 | exports->GetCreationContext().ToLocalChecked(); 16 | module->Set(context, 17 | Nan::New("exports").ToLocalChecked(), 18 | Nan::New(CreateObject) 19 | ->GetFunction(context) 20 | .ToLocalChecked()); 21 | } 22 | 23 | NODE_MODULE(addon, Init) 24 | -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/nan/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj1 = addon('hello'); 4 | const obj2 = addon('world'); 5 | console.log(obj1.msg+' '+obj2.msg); // 'hello world' -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc" ], 6 | "include_dirs": [ 7 | " 2 | #include 3 | 4 | static napi_value CreateObject(napi_env env, const napi_callback_info info) { 5 | napi_status status; 6 | 7 | size_t argc = 1; 8 | napi_value args[1]; 9 | status = napi_get_cb_info(env, info, &argc, args, NULL, NULL); 10 | assert(status == napi_ok); 11 | 12 | napi_value obj; 13 | status = napi_create_object(env, &obj); 14 | assert(status == napi_ok); 15 | 16 | status = napi_set_named_property(env, obj, "msg", args[0]); 17 | assert(status == napi_ok); 18 | 19 | return obj; 20 | } 21 | 22 | static napi_value Init(napi_env env, napi_value exports) { 23 | napi_value new_exports; 24 | napi_status status = napi_create_function( 25 | env, "", NAPI_AUTO_LENGTH, CreateObject, NULL, &new_exports); 26 | assert(status == napi_ok); 27 | return new_exports; 28 | } 29 | 30 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 31 | -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/napi/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj1 = addon('hello'); 4 | const obj2 = addon('world'); 5 | console.log(obj1.msg+' '+obj2.msg); // 'hello world' 6 | -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.c" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object_factory", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #4", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Napi::Object CreateObject(const Napi::CallbackInfo& info) { 4 | Napi::Env env = info.Env(); 5 | Napi::Object obj = Napi::Object::New(env); 6 | obj.Set(Napi::String::New(env, "msg"), info[0].ToString()); 7 | 8 | return obj; 9 | } 10 | 11 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 12 | return Napi::Function::New(env, CreateObject, "createObject"); 13 | } 14 | 15 | NODE_API_MODULE(addon, Init) 16 | -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj1 = addon('hello'); 4 | const obj2 = addon('world'); 5 | console.log(obj1.msg+' '+obj2.msg); // 'hello world' -------------------------------------------------------------------------------- /src/1-getting-started/4_object_factory/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "addon.cc" ], 8 | "include_dirs": [ 9 | " 2 | 3 | void MyFunction(const Nan::FunctionCallbackInfo& info) { 4 | info.GetReturnValue().Set(Nan::New("hello world").ToLocalChecked()); 5 | } 6 | 7 | void CreateFunction(const Nan::FunctionCallbackInfo& info) { 8 | v8::Local context = info.GetIsolate()->GetCurrentContext(); 9 | v8::Local tpl = 10 | Nan::New(MyFunction); 11 | v8::Local fn = tpl->GetFunction(context).ToLocalChecked(); 12 | 13 | // omit this to make it anonymous 14 | fn->SetName(Nan::New("theFunction").ToLocalChecked()); 15 | 16 | info.GetReturnValue().Set(fn); 17 | } 18 | 19 | void Init(v8::Local exports, v8::Local module) { 20 | Nan::SetMethod(module, "exports", CreateFunction); 21 | } 22 | 23 | NODE_MODULE(addon, Init) 24 | -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/nan/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const fn = addon(); 4 | console.log(fn()); // 'hello world' -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc" ], 6 | "include_dirs": [ 7 | " 2 | #include 3 | 4 | static napi_value MyFunction(napi_env env, napi_callback_info info) { 5 | napi_status status; 6 | 7 | napi_value str; 8 | status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, &str); 9 | assert(status == napi_ok); 10 | 11 | return str; 12 | } 13 | 14 | static napi_value CreateFunction(napi_env env, napi_callback_info info) { 15 | napi_status status; 16 | 17 | napi_value fn; 18 | status = napi_create_function( 19 | env, "theFunction", NAPI_AUTO_LENGTH, MyFunction, NULL, &fn); 20 | assert(status == napi_ok); 21 | 22 | return fn; 23 | } 24 | 25 | static napi_value Init(napi_env env, napi_value exports) { 26 | napi_value new_exports; 27 | napi_status status = napi_create_function( 28 | env, "", NAPI_AUTO_LENGTH, CreateFunction, NULL, &new_exports); 29 | assert(status == napi_ok); 30 | return new_exports; 31 | } 32 | 33 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 34 | -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/napi/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const fn = addon(); 4 | console.log(fn()); // 'hello world' 5 | -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.c" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "function_factory", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #5", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | Napi::String MyFunction(const Napi::CallbackInfo& info) { 4 | Napi::Env env = info.Env(); 5 | return Napi::String::New(env, "hello world"); 6 | } 7 | 8 | Napi::Function CreateFunction(const Napi::CallbackInfo& info) { 9 | Napi::Env env = info.Env(); 10 | Napi::Function fn = Napi::Function::New(env, MyFunction, "theFunction"); 11 | return fn; 12 | } 13 | 14 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 15 | return Napi::Function::New(env, CreateFunction, "createObject"); 16 | } 17 | 18 | NODE_API_MODULE(addon, Init) 19 | -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const fn = addon(); 4 | console.log(fn()); // 'hello world' 5 | -------------------------------------------------------------------------------- /src/1-getting-started/5_function_factory/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "addon.cc" ], 8 | "include_dirs": [ 9 | " 2 | #include "myobject.h" 3 | 4 | void InitAll(v8::Local exports) { 5 | MyObject::Init(exports); 6 | } 7 | 8 | NODE_MODULE(addon, InitAll) 9 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/nan/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj = new addon.MyObject(10); 4 | console.log( obj.plusOne() ); // 11 5 | console.log( obj.plusOne() ); // 12 6 | console.log( obj.plusOne() ); // 13 7 | 8 | console.log( obj.multiply().value() ); // 13 9 | console.log( obj.multiply(10).value() ); // 130 10 | 11 | const newobj = obj.multiply(-1); 12 | console.log( newobj.value() ); // -13 13 | console.log( obj === newobj ); // false 14 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc", "myobject.cc" ], 6 | "include_dirs": [ 7 | " 5 | 6 | class MyObject : public Nan::ObjectWrap { 7 | public: 8 | static void Init(v8::Local exports); 9 | 10 | private: 11 | explicit MyObject(double value = 0); 12 | ~MyObject(); 13 | 14 | static void New(const Nan::FunctionCallbackInfo& info); 15 | static void GetValue(const Nan::FunctionCallbackInfo& info); 16 | static void PlusOne(const Nan::FunctionCallbackInfo& info); 17 | static void Multiply(const Nan::FunctionCallbackInfo& info); 18 | static Nan::Persistent constructor; 19 | double value_; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/nan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object_wrap", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #6", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0", 10 | "nan": "^2.14.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/napi/addon.cc: -------------------------------------------------------------------------------- 1 | #include "myobject.h" 2 | 3 | napi_value Init(napi_env env, napi_value exports) { 4 | return MyObject::Init(env, exports); 5 | } 6 | 7 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 8 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/napi/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj = new addon.MyObject(10); 4 | console.log( obj.plusOne() ); // 11 5 | console.log( obj.plusOne() ); // 12 6 | console.log( obj.plusOne() ); // 13 7 | 8 | console.log( obj.multiply().value ); // 13 9 | console.log( obj.multiply(10).value ); // 130 10 | 11 | const newobj = obj.multiply(-1); 12 | console.log( newobj.value ); // -13 13 | console.log( obj === newobj ); // false 14 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc", "myobject.cc" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/napi/myobject.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ 2 | #define TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ 3 | 4 | #include 5 | 6 | class MyObject { 7 | public: 8 | static napi_value Init(napi_env env, napi_value exports); 9 | static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); 10 | 11 | private: 12 | explicit MyObject(double value_ = 0); 13 | ~MyObject(); 14 | 15 | static napi_value New(napi_env env, napi_callback_info info); 16 | static napi_value GetValue(napi_env env, napi_callback_info info); 17 | static napi_value SetValue(napi_env env, napi_callback_info info); 18 | static napi_value PlusOne(napi_env env, napi_callback_info info); 19 | static napi_value Multiply(napi_env env, napi_callback_info info); 20 | static inline napi_value Constructor(napi_env env); 21 | 22 | double value_; 23 | napi_env env_; 24 | napi_ref wrapper_; 25 | }; 26 | 27 | #endif // TEST_ADDONS_NAPI_6_OBJECT_WRAP_MYOBJECT_H_ 28 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object_wrap", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #6", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myobject.h" 3 | 4 | Napi::Object InitAll(Napi::Env env, Napi::Object exports) { 5 | return MyObject::Init(env, exports); 6 | } 7 | 8 | NODE_API_MODULE(addon, InitAll) 9 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj = new addon.MyObject(10); 4 | console.log( obj.plusOne() ); // 11 5 | console.log( obj.plusOne() ); // 12 6 | console.log( obj.plusOne() ); // 13 7 | 8 | console.log( obj.multiply().value() ); // 13 9 | console.log( obj.multiply(10).value() ); // 130 10 | 11 | const newobj = obj.multiply(-1); 12 | console.log( newobj.value() ); // -13 13 | console.log( obj === newobj ); // false 14 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "addon.cc", "myobject.cc" ], 8 | "include_dirs": [ 9 | " 5 | 6 | class MyObject : public Napi::ObjectWrap { 7 | public: 8 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 9 | MyObject(const Napi::CallbackInfo& info); 10 | 11 | private: 12 | Napi::Value GetValue(const Napi::CallbackInfo& info); 13 | Napi::Value PlusOne(const Napi::CallbackInfo& info); 14 | Napi::Value Multiply(const Napi::CallbackInfo& info); 15 | 16 | double value_; 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/1-getting-started/6_object_wrap/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "object_wrap", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #6", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "engines": { 9 | "node": "~10 >=10.20 || >=12.17" 10 | }, 11 | "dependencies": { 12 | "bindings": "~1.5.0", 13 | "node-addon-api": "^8.1.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/nan/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myobject.h" 3 | 4 | void CreateObject(const Nan::FunctionCallbackInfo& info) { 5 | info.GetReturnValue().Set(MyObject::NewInstance(info[0])); 6 | } 7 | 8 | void InitAll(v8::Local exports, v8::Local module) { 9 | v8::Local context = 10 | exports->GetCreationContext().ToLocalChecked(); 11 | 12 | Nan::HandleScope scope; 13 | 14 | MyObject::Init(); 15 | 16 | module->Set(context, 17 | Nan::New("exports").ToLocalChecked(), 18 | Nan::New(CreateObject) 19 | ->GetFunction(context) 20 | .ToLocalChecked()); 21 | } 22 | 23 | NODE_MODULE(addon, InitAll) 24 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/nan/addon.js: -------------------------------------------------------------------------------- 1 | const createObject = require('bindings')('addon'); 2 | 3 | const obj = createObject(10); 4 | console.log( obj.plusOne() ); // 11 5 | console.log( obj.plusOne() ); // 12 6 | console.log( obj.plusOne() ); // 13 7 | 8 | const obj2 = createObject(20); 9 | console.log( obj2.plusOne() ); // 21 10 | console.log( obj2.plusOne() ); // 22 11 | console.log( obj2.plusOne() ); // 23 12 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc", "myobject.cc" ], 6 | "include_dirs": [ 7 | " 5 | 6 | class MyObject : public Nan::ObjectWrap { 7 | public: 8 | static void Init(); 9 | static v8::Local NewInstance(v8::Local arg); 10 | 11 | private: 12 | MyObject(); 13 | ~MyObject(); 14 | 15 | static Nan::Persistent constructor; 16 | static void New(const Nan::FunctionCallbackInfo& info); 17 | static void PlusOne(const Nan::FunctionCallbackInfo& info); 18 | double counter_; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/nan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "factory_wrap", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #7", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0", 10 | "nan": "^2.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/napi/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myobject.h" 3 | 4 | napi_value CreateObject(napi_env env, napi_callback_info info) { 5 | napi_status status; 6 | 7 | size_t argc = 1; 8 | napi_value args[1]; 9 | status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 10 | assert(status == napi_ok); 11 | 12 | napi_value instance; 13 | status = MyObject::NewInstance(env, args[0], &instance); 14 | assert(status == napi_ok); 15 | 16 | return instance; 17 | } 18 | 19 | napi_value Init(napi_env env, napi_value exports) { 20 | napi_status status = MyObject::Init(env); 21 | assert(status == napi_ok); 22 | 23 | napi_value new_exports; 24 | status = napi_create_function( 25 | env, "", NAPI_AUTO_LENGTH, CreateObject, nullptr, &new_exports); 26 | assert(status == napi_ok); 27 | return new_exports; 28 | } 29 | 30 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 31 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/napi/addon.js: -------------------------------------------------------------------------------- 1 | const createObject = require('bindings')('addon'); 2 | 3 | const obj = createObject(10); 4 | console.log( obj.plusOne() ); // 11 5 | console.log( obj.plusOne() ); // 12 6 | console.log( obj.plusOne() ); // 13 7 | 8 | const obj2 = createObject(20); 9 | console.log( obj2.plusOne() ); // 21 10 | console.log( obj2.plusOne() ); // 22 11 | console.log( obj2.plusOne() ); // 23 12 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc", "myobject.cc" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/napi/myobject.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ 2 | #define TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ 3 | 4 | #include 5 | 6 | class MyObject { 7 | public: 8 | static napi_status Init(napi_env env); 9 | static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); 10 | static napi_status NewInstance(napi_env env, 11 | napi_value arg, 12 | napi_value* instance); 13 | 14 | private: 15 | MyObject(); 16 | ~MyObject(); 17 | 18 | static inline napi_value Constructor(napi_env env); 19 | static napi_value New(napi_env env, napi_callback_info info); 20 | static napi_value PlusOne(napi_env env, napi_callback_info info); 21 | double counter_; 22 | napi_env env_; 23 | napi_ref wrapper_; 24 | }; 25 | 26 | #endif // TEST_ADDONS_NAPI_7_FACTORY_WRAP_MYOBJECT_H_ 27 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "factory_wrap", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #7", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myobject.h" 3 | 4 | Napi::Object CreateObject(const Napi::CallbackInfo& info) { 5 | return MyObject::NewInstance(info.Env(), info[0]); 6 | } 7 | 8 | Napi::Object InitAll(Napi::Env env, Napi::Object exports) { 9 | Napi::Object new_exports = 10 | Napi::Function::New(env, CreateObject, "CreateObject"); 11 | return MyObject::Init(env, new_exports); 12 | } 13 | 14 | NODE_API_MODULE(addon, InitAll) 15 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const createObject = require('bindings')('addon'); 2 | 3 | const obj = createObject(10); 4 | console.log( obj.plusOne() ); // 11 5 | console.log( obj.plusOne() ); // 12 6 | console.log( obj.plusOne() ); // 13 7 | 8 | const obj2 = createObject(20); 9 | console.log( obj2.plusOne() ); // 21 10 | console.log( obj2.plusOne() ); // 22 11 | console.log( obj2.plusOne() ); // 23 12 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "addon.cc", "myobject.cc" ], 8 | "include_dirs": [ 9 | " 3 | #include 4 | 5 | using namespace Napi; 6 | 7 | Napi::Object MyObject::Init(Napi::Env env, Napi::Object exports) { 8 | Napi::Function func = DefineClass( 9 | env, "MyObject", {InstanceMethod("plusOne", &MyObject::PlusOne)}); 10 | 11 | Napi::FunctionReference* constructor = new Napi::FunctionReference(); 12 | *constructor = Napi::Persistent(func); 13 | env.SetInstanceData(constructor); 14 | 15 | exports.Set("MyObject", func); 16 | return exports; 17 | } 18 | 19 | MyObject::MyObject(const Napi::CallbackInfo& info) 20 | : Napi::ObjectWrap(info) { 21 | this->counter_ = info[0].As().DoubleValue(); 22 | }; 23 | 24 | Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg) { 25 | Napi::EscapableHandleScope scope(env); 26 | Napi::Object obj = env.GetInstanceData()->New({arg}); 27 | return scope.Escape(napi_value(obj)).ToObject(); 28 | } 29 | 30 | Napi::Value MyObject::PlusOne(const Napi::CallbackInfo& info) { 31 | Napi::Env env = info.Env(); 32 | this->counter_ = this->counter_ + 1; 33 | 34 | return Napi::Number::New(env, this->counter_); 35 | } 36 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/node-addon-api/myobject.h: -------------------------------------------------------------------------------- 1 | #ifndef MYOBJECT_H 2 | #define MYOBJECT_H 3 | 4 | #include 5 | 6 | class MyObject : public Napi::ObjectWrap { 7 | public: 8 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 9 | static Napi::Object NewInstance(Napi::Env env, Napi::Value arg); 10 | MyObject(const Napi::CallbackInfo& info); 11 | 12 | private: 13 | Napi::Value PlusOne(const Napi::CallbackInfo& info); 14 | double counter_; 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/1-getting-started/7_factory_wrap/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "factory_wrap", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #7", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "engines": { 9 | "node": "~10 >=10.20 || >=12.17" 10 | }, 11 | "dependencies": { 12 | "bindings": "~1.5.0", 13 | "node-addon-api": "^8.1.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/1-getting-started/a-first-project/node-addon-api/README.md: -------------------------------------------------------------------------------- 1 | # Node-API A First Project 2 | 3 | This is an example project that accompanies the Node-API workshop tutorials 4 | 5 | A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/getting-started/first.html). 6 | 7 | To build and run this program on your system, clone it to your computer and run these two commands inside your clone: 8 | 9 | ``` 10 | npm install 11 | npm test 12 | ``` 13 | 14 | > You need to have Node 10.5.0 or later installed. -------------------------------------------------------------------------------- /src/1-getting-started/a-first-project/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'hello-world-native', 5 | 'sources': [ 'src/hello_world.cc' ], 6 | 'include_dirs': [" 2 | 3 | using namespace Napi; 4 | 5 | Napi::String Method(const Napi::CallbackInfo& info) { 6 | Napi::Env env = info.Env(); 7 | return Napi::String::New(env, "world"); 8 | } 9 | 10 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 11 | exports.Set(Napi::String::New(env, "HelloWorld"), 12 | Napi::Function::New(env, Method)); 13 | return exports; 14 | } 15 | 16 | NODE_API_MODULE(addon, Init) 17 | -------------------------------------------------------------------------------- /src/1-getting-started/a-first-project/node-addon-api/test/test_binding.js: -------------------------------------------------------------------------------- 1 | const HelloWorld = require("../lib/binding.js"); 2 | const assert = require("assert"); 3 | 4 | assert(HelloWorld, "The expected function is undefined"); 5 | 6 | function testBasic() 7 | { 8 | const result = HelloWorld("hello"); 9 | assert.strictEqual(result, "world", "Unexpected value returned"); 10 | } 11 | 12 | assert.doesNotThrow(testBasic, undefined, "testBasic threw an expection"); 13 | 14 | console.log("Tests passed- everything looks OK!"); -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/nan/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myobject.h" 3 | 4 | using namespace v8; 5 | 6 | void CreateObject(const Nan::FunctionCallbackInfo& info) { 7 | info.GetReturnValue().Set(MyObject::NewInstance(info[0])); 8 | } 9 | 10 | void Add(const Nan::FunctionCallbackInfo& info) { 11 | v8::Local context = info.GetIsolate()->GetCurrentContext(); 12 | 13 | MyObject* obj1 = Nan::ObjectWrap::Unwrap( 14 | info[0]->ToObject(context).ToLocalChecked()); 15 | MyObject* obj2 = Nan::ObjectWrap::Unwrap( 16 | info[1]->ToObject(context).ToLocalChecked()); 17 | double sum = obj1->Val() + obj2->Val(); 18 | info.GetReturnValue().Set(Nan::New(sum)); 19 | } 20 | 21 | void InitAll(v8::Local exports) { 22 | v8::Local context = 23 | exports->GetCreationContext().ToLocalChecked(); 24 | 25 | MyObject::Init(); 26 | 27 | exports->Set(context, 28 | Nan::New("createObject").ToLocalChecked(), 29 | Nan::New(CreateObject) 30 | ->GetFunction(context) 31 | .ToLocalChecked()); 32 | 33 | exports->Set(context, 34 | Nan::New("add").ToLocalChecked(), 35 | Nan::New(Add) 36 | ->GetFunction(context) 37 | .ToLocalChecked()); 38 | } 39 | 40 | NODE_MODULE(addon, InitAll) 41 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/nan/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj1 = addon.createObject(10); 4 | const obj2 = addon.createObject(20); 5 | const result = addon.add(obj1, obj2); 6 | 7 | console.log(result); // 30 8 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc", "myobject.cc" ], 6 | "include_dirs": [ 7 | " 3 | 4 | MyObject::MyObject(){}; 5 | MyObject::~MyObject(){}; 6 | 7 | Nan::Persistent MyObject::constructor; 8 | 9 | void MyObject::Init() { 10 | Nan::HandleScope scope; 11 | 12 | // Prepare constructor template 13 | v8::Local tpl = Nan::New(New); 14 | tpl->SetClassName(Nan::New("MyObject").ToLocalChecked()); 15 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 16 | 17 | constructor.Reset( 18 | tpl->GetFunction(Nan::GetCurrentContext()).ToLocalChecked()); 19 | } 20 | 21 | void MyObject::New(const Nan::FunctionCallbackInfo& info) { 22 | v8::Local context = info.GetIsolate()->GetCurrentContext(); 23 | MyObject* obj = new MyObject(); 24 | obj->val_ = 25 | info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust(); 26 | obj->Wrap(info.This()); 27 | 28 | info.GetReturnValue().Set(info.This()); 29 | } 30 | 31 | v8::Local MyObject::NewInstance(v8::Local arg) { 32 | Nan::EscapableHandleScope scope; 33 | 34 | const unsigned argc = 1; 35 | v8::Local argv[argc] = {arg}; 36 | v8::Local cons = Nan::New(constructor); 37 | v8::Local context = 38 | v8::Isolate::GetCurrent()->GetCurrentContext(); 39 | v8::Local instance = 40 | cons->NewInstance(context, argc, argv).ToLocalChecked(); 41 | 42 | return scope.Escape(instance); 43 | } 44 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/nan/myobject.h: -------------------------------------------------------------------------------- 1 | #ifndef MYOBJECT_H 2 | #define MYOBJECT_H 3 | 4 | #include 5 | 6 | class MyObject : public Nan::ObjectWrap { 7 | public: 8 | static void Init(); 9 | static v8::Local NewInstance(v8::Local arg); 10 | double Val() const { return val_; } 11 | 12 | private: 13 | MyObject(); 14 | ~MyObject(); 15 | 16 | static Nan::Persistent constructor; 17 | static void New(const Nan::FunctionCallbackInfo& info); 18 | double val_; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/nan/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passing_wrapped", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #8", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0", 10 | "nan": "^2.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/napi/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myobject.h" 3 | 4 | napi_value CreateObject(napi_env env, napi_callback_info info) { 5 | napi_status status; 6 | 7 | size_t argc = 1; 8 | napi_value args[1]; 9 | status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 10 | assert(status == napi_ok); 11 | 12 | napi_value instance; 13 | status = MyObject::NewInstance(env, args[0], &instance); 14 | 15 | return instance; 16 | } 17 | 18 | napi_value Add(napi_env env, napi_callback_info info) { 19 | napi_status status; 20 | 21 | size_t argc = 2; 22 | napi_value args[2]; 23 | status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 24 | assert(status == napi_ok); 25 | 26 | MyObject* obj1; 27 | status = napi_unwrap(env, args[0], reinterpret_cast(&obj1)); 28 | assert(status == napi_ok); 29 | 30 | MyObject* obj2; 31 | status = napi_unwrap(env, args[1], reinterpret_cast(&obj2)); 32 | assert(status == napi_ok); 33 | 34 | napi_value sum; 35 | status = napi_create_double(env, obj1->Val() + obj2->Val(), &sum); 36 | assert(status == napi_ok); 37 | 38 | return sum; 39 | } 40 | 41 | #define DECLARE_NAPI_METHOD(name, func) \ 42 | { name, 0, func, 0, 0, 0, napi_default, 0 } 43 | 44 | napi_value Init(napi_env env, napi_value exports) { 45 | napi_status status; 46 | 47 | MyObject::Init(env); 48 | 49 | napi_property_descriptor desc[] = { 50 | DECLARE_NAPI_METHOD("createObject", CreateObject), 51 | DECLARE_NAPI_METHOD("add", Add), 52 | }; 53 | status = 54 | napi_define_properties(env, exports, sizeof(desc) / sizeof(*desc), desc); 55 | assert(status == napi_ok); 56 | return exports; 57 | } 58 | 59 | NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 60 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/napi/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj1 = addon.createObject(10); 4 | const obj2 = addon.createObject(20); 5 | const result = addon.add(obj1, obj2); 6 | 7 | console.log(result); // 30 8 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "addon.cc", "myobject.cc" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/napi/myobject.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ 2 | #define TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ 3 | 4 | #include 5 | 6 | class MyObject { 7 | public: 8 | static napi_status Init(napi_env env); 9 | static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); 10 | static napi_status NewInstance(napi_env env, 11 | napi_value arg, 12 | napi_value* instance); 13 | double Val() const { return val_; } 14 | 15 | private: 16 | MyObject(); 17 | ~MyObject(); 18 | 19 | static inline napi_value Constructor(napi_env env); 20 | static napi_value New(napi_env env, napi_callback_info info); 21 | double val_; 22 | napi_env env_; 23 | napi_ref wrapper_; 24 | }; 25 | 26 | #endif // TEST_ADDONS_NAPI_8_PASSING_WRAPPED_MYOBJECT_H_ 27 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passing_wrapped", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #8", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "dependencies": { 9 | "bindings": "~1.5.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myobject.h" 3 | 4 | using namespace Napi; 5 | 6 | Napi::Object CreateObject(const Napi::CallbackInfo& info) { 7 | return MyObject::NewInstance(info.Env(), info[0]); 8 | } 9 | 10 | Napi::Number Add(const Napi::CallbackInfo& info) { 11 | Napi::Env env = info.Env(); 12 | MyObject* obj1 = 13 | Napi::ObjectWrap::Unwrap(info[0].As()); 14 | MyObject* obj2 = 15 | Napi::ObjectWrap::Unwrap(info[1].As()); 16 | double sum = obj1->Val() + obj2->Val(); 17 | return Napi::Number::New(env, sum); 18 | } 19 | 20 | Napi::Object InitAll(Napi::Env env, Napi::Object exports) { 21 | MyObject::Init(env, exports); 22 | 23 | exports.Set(Napi::String::New(env, "createObject"), 24 | Napi::Function::New(env, CreateObject)); 25 | 26 | exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add)); 27 | 28 | return exports; 29 | } 30 | 31 | NODE_API_MODULE(addon, InitAll) 32 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | 3 | const obj1 = addon.createObject(10); 4 | const obj2 = addon.createObject(20); 5 | const result = addon.add(obj1, obj2); 6 | 7 | console.log(result); // 30 8 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "cflags!": [ "-fno-exceptions" ], 8 | "cflags_cc!": [ "-fno-exceptions" ], 9 | "sources": [ "addon.cc", "myobject.cc" ], 10 | "include_dirs": [ 11 | " 3 | #include 4 | 5 | MyObject::MyObject(const Napi::CallbackInfo& info) 6 | : Napi::ObjectWrap(info) { 7 | this->val_ = info[0].As().DoubleValue(); 8 | }; 9 | 10 | void MyObject::Init(Napi::Env env, Napi::Object exports) { 11 | Napi::Function func = DefineClass(env, "MyObject", {}); 12 | 13 | Napi::FunctionReference* constructor = new Napi::FunctionReference(); 14 | *constructor = Napi::Persistent(func); 15 | env.SetInstanceData(constructor); // NOTE: this assumes only 1 class is 16 | // exported for multiple exported classes, 17 | // need a struct or other mechanism 18 | 19 | exports.Set("MyObject", func); 20 | } 21 | 22 | Napi::Object MyObject::NewInstance(Napi::Env env, Napi::Value arg) { 23 | Napi::Object obj = env.GetInstanceData()->New({arg}); 24 | return obj; 25 | } 26 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/myobject.h: -------------------------------------------------------------------------------- 1 | #ifndef MYOBJECT_H 2 | #define MYOBJECT_H 3 | 4 | #include 5 | 6 | class MyObject : public Napi::ObjectWrap { 7 | public: 8 | static void Init(Napi::Env env, Napi::Object exports); 9 | static Napi::Object NewInstance(Napi::Env env, Napi::Value arg); 10 | double Val() const { return val_; } 11 | MyObject(const Napi::CallbackInfo& info); 12 | 13 | private: 14 | double val_; 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/8_passing_wrapped/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passing_wrapped", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons Example #8", 5 | "main": "addon.js", 6 | "private": true, 7 | "gypfile": true, 8 | "engines": { 9 | "node": "~10 >=10.20 || >=12.17" 10 | }, 11 | "dependencies": { 12 | "bindings": "~1.5.0", 13 | "node-addon-api": "^8.1.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/array_buffer_to_native/node-addon-api/array_buffer_to_native.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void ArrayConsumer(const int32_t* array, size_t length) { 5 | for (size_t index = 0; index < length; index++) { 6 | fprintf(stderr, "array[%lu] = %d\n", index, array[index]); 7 | } 8 | } 9 | 10 | static Napi::Value AcceptArrayBuffer(const Napi::CallbackInfo& info) { 11 | if (info.Length() != 1) { 12 | Napi::Error::New(info.Env(), "Expected exactly one argument") 13 | .ThrowAsJavaScriptException(); 14 | return info.Env().Undefined(); 15 | } 16 | if (!info[0].IsArrayBuffer()) { 17 | Napi::Error::New(info.Env(), "Expected an ArrayBuffer") 18 | .ThrowAsJavaScriptException(); 19 | return info.Env().Undefined(); 20 | } 21 | 22 | Napi::ArrayBuffer buf = info[0].As(); 23 | 24 | ArrayConsumer(reinterpret_cast(buf.Data()), 25 | buf.ByteLength() / sizeof(int32_t)); 26 | 27 | return info.Env().Undefined(); 28 | } 29 | 30 | static Napi::Object Init(Napi::Env env, Napi::Object exports) { 31 | exports["AcceptArrayBuffer"] = Napi::Function::New(env, AcceptArrayBuffer); 32 | return exports; 33 | } 34 | 35 | NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) 36 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/array_buffer_to_native/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "array_buffer_to_native", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "array_buffer_to_native.cc" ], 8 | "include_dirs": [ 9 | " You need to have Node 10.5.0 or later installed. -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'object-wrap-demo-native', 5 | 'sources': [ 'src/object_wrap_demo.cc' ], 6 | 'include_dirs': [" 4 | 5 | class ObjectWrapDemo : public Napi::ObjectWrap { 6 | public: 7 | ObjectWrapDemo(const Napi::CallbackInfo&); 8 | Napi::Value Greet(const Napi::CallbackInfo&); 9 | 10 | static Napi::Function GetClass(Napi::Env); 11 | 12 | private: 13 | std::string _greeterName; 14 | }; 15 | -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/object-wrap-demo/node-addon-api/test/test_binding.js: -------------------------------------------------------------------------------- 1 | const ObjectWrapDemo = require("../lib/binding.js"); 2 | const assert = require("assert"); 3 | 4 | assert(ObjectWrapDemo, "The expected module is undefined"); 5 | 6 | function testBasic() 7 | { 8 | const instance = new ObjectWrapDemo("mr-yeoman"); 9 | assert(instance.greet, "The expected method is not defined"); 10 | assert.strictEqual(instance.greet("kermit"), "mr-yeoman", "Unexpected value returned"); 11 | } 12 | 13 | function testInvalidParams() 14 | { 15 | const instance = new ObjectWrapDemo(); 16 | } 17 | 18 | assert.doesNotThrow(testBasic, undefined, "testBasic threw an expection"); 19 | assert.throws(testInvalidParams, undefined, "testInvalidParams didn't throw"); 20 | 21 | console.log("Tests passed- everything looks OK!"); -------------------------------------------------------------------------------- /src/2-js-to-native-conversion/typed_array_to_native/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "typed_array_to_native", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "sources": [ "typed_array_to_native.cc" ], 8 | "include_dirs": [ 9 | "= 0) { 23 | if (Math.random() < 0.5) { 24 | console.log(prefix + ': new value (decremented): ' + addon.decrement()); 25 | } else { 26 | console.log(prefix + ': new value (incremented): ' + addon.increment()); 27 | } 28 | setImmediate(() => useAddon(addon, prefix, --iterations)); 29 | } 30 | } 31 | 32 | if (isMainThread) { 33 | // On the main thread, we launch a worker and wait for it to come online. Then 34 | // we start the loop. 35 | (new Worker(__filename)).on('online', 36 | () => useAddon(addon, "Main thread", iterations)); 37 | } else { 38 | // On the secondary thread we immediately start the loop. 39 | useAddon(addon, "Worker thread", iterations); 40 | } 41 | -------------------------------------------------------------------------------- /src/3-context-awareness/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multiple_load", 3 | "version": "0.0.0", 4 | "description": "Multiple load example", 5 | "main": "index.js", 6 | "private": true, 7 | "scripts": { 8 | "test": "node --experimental-worker index.js" 9 | }, 10 | "engines": { 11 | "node": ">= 10.10.0" 12 | }, 13 | "gypfile": true, 14 | "dependencies": { 15 | "bindings": "~1.5.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/3-context-awareness/node_10/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'multiple_load', 5 | 'sources': [ 'multiple_load.cc' ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/3-context-awareness/node_10/index.js: -------------------------------------------------------------------------------- 1 | // Example illustrating the case where a native addon is loaded multiple times. 2 | // This entire file is executed twice, concurrently - once on the main thread, 3 | // and once on a thread launched from the main thread. 4 | 5 | // We load the worker threads module, which allows us to launch multiple Node.js 6 | // environments, each in its own thread. 7 | const { 8 | Worker, isMainThread 9 | } = require('worker_threads'); 10 | 11 | // We load the native addon. 12 | const addon = require('bindings')('multiple_load'); 13 | 14 | // The iteration count can be tweaked to ensure that the output from the two 15 | // threads is interleaved. Too few iterations and the output of one thread 16 | // follows the output of the other, not really illustrating the concurrency. 17 | const iterations = 1000; 18 | 19 | // This function is an idle loop that performs a random walk from 0 by calling 20 | // into the native addon to either increment or decrement the initial value. 21 | function useAddon(addon, prefix, iterations) { 22 | if (iterations >= 0) { 23 | if (Math.random() < 0.5) { 24 | console.log(prefix + ': new value (decremented): ' + addon.decrement()); 25 | } else { 26 | console.log(prefix + ': new value (incremented): ' + addon.increment()); 27 | } 28 | setImmediate(() => useAddon(addon, prefix, --iterations)); 29 | } 30 | } 31 | 32 | if (isMainThread) { 33 | // On the main thread, we launch a worker and wait for it to come online. Then 34 | // we start the loop. 35 | (new Worker(__filename)).on('online', 36 | () => useAddon(addon, "Main thread", iterations)); 37 | } else { 38 | // On the secondary thread we immediately start the loop. 39 | useAddon(addon, "Worker thread", iterations); 40 | } 41 | -------------------------------------------------------------------------------- /src/3-context-awareness/node_10/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multiple_load", 3 | "version": "0.0.0", 4 | "description": "Multiple load example", 5 | "main": "index.js", 6 | "private": true, 7 | "scripts": { 8 | "test": "node --experimental-worker index.js" 9 | }, 10 | "engines": { 11 | "node": ">= 10.10.0" 12 | }, 13 | "gypfile": true, 14 | "dependencies": { 15 | "bindings": "~1.5.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/4-references-and-handle-scope/function-reference-demo/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ 6 | "src/binding.cc", 7 | "src/native-addon.cc" 8 | ], 9 | 'cflags!': [ '-fno-exceptions' ], 10 | 'cflags_cc!': [ '-fno-exceptions' ], 11 | 'include_dirs': [" 2 | #include "native-addon.h" 3 | 4 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 5 | NativeAddon::Init(env, exports); 6 | return exports; 7 | } 8 | 9 | NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) -------------------------------------------------------------------------------- /src/4-references-and-handle-scope/function-reference-demo/node-addon-api/src/native-addon.cc: -------------------------------------------------------------------------------- 1 | #include "native-addon.h" 2 | #include 3 | 4 | Napi::FunctionReference NativeAddon::constructor; 5 | 6 | Napi::Object NativeAddon::Init(Napi::Env env, Napi::Object exports) { 7 | Napi::Function func = 8 | DefineClass(env, 9 | "NativeAddon", 10 | {InstanceMethod("tryCallByStoredReference", 11 | &NativeAddon::TryCallByStoredReference), 12 | InstanceMethod("tryCallByStoredFunction", 13 | &NativeAddon::TryCallByStoredFunction)}); 14 | 15 | constructor = Napi::Persistent(func); 16 | constructor.SuppressDestruct(); 17 | 18 | exports.Set("NativeAddon", func); 19 | return exports; 20 | } 21 | 22 | NativeAddon::NativeAddon(const Napi::CallbackInfo& info) 23 | : Napi::ObjectWrap(info) { 24 | jsFnRef = Napi::Persistent(info[0].As()); 25 | jsFn = info[1].As(); 26 | } 27 | 28 | void NativeAddon::TryCallByStoredReference(const Napi::CallbackInfo& info) { 29 | // Napi::Env env = info.Env(); 30 | jsFnRef.Call({}); 31 | } 32 | 33 | void NativeAddon::TryCallByStoredFunction(const Napi::CallbackInfo& info) { 34 | // Napi::Env env = info.Env(); 35 | jsFn.Call({}); 36 | } -------------------------------------------------------------------------------- /src/4-references-and-handle-scope/function-reference-demo/node-addon-api/src/native-addon.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class NativeAddon : public Napi::ObjectWrap { 4 | public: 5 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 6 | NativeAddon(const Napi::CallbackInfo& info); 7 | 8 | private: 9 | static Napi::FunctionReference constructor; 10 | Napi::FunctionReference jsFnRef; 11 | Napi::Function jsFn; 12 | 13 | void TryCallByStoredReference(const Napi::CallbackInfo& info); 14 | void TryCallByStoredFunction(const Napi::CallbackInfo& info); 15 | }; -------------------------------------------------------------------------------- /src/5-async-work/async-iterator/node-addon-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (example) 2 | include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/) 3 | cmake_minimum_required(VERSION 3.18) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | include_directories(${CMAKE_JS_INC}) 8 | file(GLOB SOURCE_FILES "*.cc") 9 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) 10 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") 11 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) 12 | 13 | # Include Node-API wrappers 14 | execute_process(COMMAND node -p "require('node-addon-api').include" 15 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 16 | OUTPUT_VARIABLE NODE_ADDON_API_DIR 17 | ) 18 | string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) 19 | 20 | target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR}) 21 | 22 | # define NAPI_VERSION 23 | add_definitions(-DNAPI_VERSION=6) 24 | -------------------------------------------------------------------------------- /src/5-async-work/async-iterator/node-addon-api/index.js: -------------------------------------------------------------------------------- 1 | const { AsyncIteratorExample } = require('bindings')('example'); 2 | 3 | async function main(from, to) { 4 | const iterator = new AsyncIteratorExample(from, to); 5 | for await (const value of iterator) { 6 | console.log(value); 7 | } 8 | } 9 | 10 | /* 11 | // The JavaScript equivalent of the node-addon-api C++ code for reference 12 | async function main(from, to) { 13 | class AsyncIteratorExample { 14 | constructor(from, to) { 15 | this.from = from; 16 | this.to = to; 17 | } 18 | 19 | [Symbol.asyncIterator]() { 20 | return { 21 | current: this.from, 22 | last: this.to, 23 | next() { 24 | return new Promise(resolve => { 25 | setTimeout(() => { 26 | if (this.current <= this.last) { 27 | resolve({ done: false, value: this.current++ }); 28 | } else { 29 | resolve({ done: true }); 30 | } 31 | }, 1000) 32 | }); 33 | } 34 | } 35 | } 36 | } 37 | const iterator = new AsyncIteratorExample(from, to); 38 | 39 | for await (const value of iterator) { 40 | console.log(value); 41 | } 42 | } 43 | */ 44 | 45 | main(0, 5) 46 | .catch(e => { 47 | console.error(e); 48 | process.exit(1); 49 | }); 50 | -------------------------------------------------------------------------------- /src/5-async-work/async-iterator/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async-iterator-example", 3 | "version": "0.0.0", 4 | "description": "Async iterator example using node-addon-api", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "^1.5.0", 9 | "cmake-js": "^7.3.0", 10 | "node-addon-api": "^8.1.0" 11 | }, 12 | "scripts": { 13 | "test": "node index.js", 14 | "install": "cmake-js compile" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/README.md: -------------------------------------------------------------------------------- 1 | In this directory run `node-gyp rebuild` and then `node ./addon.js` -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "async.h" // NOLINT(build/include) 3 | #include "sync.h" // NOLINT(build/include) 4 | 5 | using Nan::GetFunction; 6 | using Nan::New; 7 | using Nan::Set; 8 | using v8::FunctionTemplate; 9 | using v8::Object; 10 | using v8::String; 11 | 12 | // Expose synchronous and asynchronous access to our 13 | // Estimate() function 14 | NAN_MODULE_INIT(InitAll) { 15 | Set(target, 16 | New("calculateSync").ToLocalChecked(), 17 | GetFunction(New(CalculateSync)).ToLocalChecked()); 18 | 19 | Set(target, 20 | New("calculateAsync").ToLocalChecked(), 21 | GetFunction(New(CalculateAsync)).ToLocalChecked()); 22 | } 23 | 24 | NODE_MODULE(addon, InitAll) 25 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('./build/Release/addon'); 2 | const calculations = process.argv[2] || 100000000; 3 | 4 | function printResult(type, pi, ms) { 5 | console.log(type, 'method:'); 6 | console.log('\tπ ≈ ' + pi + 7 | ' (' + Math.abs(pi - Math.PI) + ' away from actual)'); 8 | console.log('\tTook ' + ms + 'ms'); 9 | console.log(); 10 | } 11 | 12 | function runSync() { 13 | const start = Date.now(); 14 | // Estimate() will execute in the current thread, 15 | // the next line won't return until it is finished 16 | const result = addon.calculateSync(calculations); 17 | printResult('Sync', result, Date.now() - start); 18 | } 19 | 20 | function runAsync() { 21 | // how many batches should we split the work in to? 22 | const batches = process.argv[3] || 16; 23 | let ended = 0; 24 | let total = 0; 25 | const start = Date.now(); 26 | 27 | function done (err, result) { 28 | total += result; 29 | 30 | // have all the batches finished executing? 31 | if (++ended === batches) { 32 | printResult('Async', total / batches, Date.now() - start); 33 | } 34 | } 35 | 36 | // for each batch of work, request an async Estimate() for 37 | // a portion of the total number of calculations 38 | for (let i = 0; i < batches; i++) { 39 | addon.calculateAsync(calculations / batches, done); 40 | } 41 | } 42 | 43 | runSync(); 44 | runAsync(); 45 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/async.cc: -------------------------------------------------------------------------------- 1 | #include "async.h" // NOLINT(build/include) 2 | #include 3 | #include "pi_est.h" // NOLINT(build/include) 4 | 5 | using Nan::AsyncQueueWorker; 6 | using Nan::AsyncWorker; 7 | using Nan::Callback; 8 | using Nan::HandleScope; 9 | using Nan::New; 10 | using Nan::Null; 11 | using Nan::To; 12 | using v8::Function; 13 | using v8::Local; 14 | using v8::Number; 15 | using v8::Value; 16 | 17 | class PiWorker : public AsyncWorker { 18 | public: 19 | PiWorker(Callback* callback, int points) 20 | : AsyncWorker(callback), points(points), estimate(0) {} 21 | ~PiWorker() {} 22 | 23 | // Executed inside the worker-thread. 24 | // It is not safe to access V8, or V8 data structures 25 | // here, so everything we need for input and output 26 | // should go on `this`. 27 | void Execute() { estimate = Estimate(points); } 28 | 29 | // Executed when the async work is complete 30 | // this function will be run inside the main event loop 31 | // so it is safe to use V8 again 32 | void HandleOKCallback() { 33 | HandleScope scope; 34 | 35 | Local argv[] = {Null(), New(estimate)}; 36 | 37 | callback->Call(2, argv, async_resource); 38 | } 39 | 40 | private: 41 | int points; 42 | double estimate; 43 | }; 44 | 45 | // Asynchronous access to the `Estimate()` function 46 | NAN_METHOD(CalculateAsync) { 47 | int points = To(info[0]).FromJust(); 48 | Callback* callback = new Callback(To(info[1]).ToLocalChecked()); 49 | 50 | AsyncQueueWorker(new PiWorker(callback, points)); 51 | } 52 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/async.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_ 2 | #define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_ 3 | 4 | #include 5 | 6 | NAN_METHOD(CalculateAsync); 7 | 8 | #endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_ 9 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ 6 | "addon.cc", 7 | "pi_est.cc", 8 | "sync.cc", 9 | "async.cc" 10 | ], 11 | "include_dirs": [" 3 | 4 | /* 5 | Estimate the value of π by using a Monte Carlo method. 6 | Take `points` samples of random x and y values on a 7 | [0,1][0,1] plane. Calculating the length of the diagonal 8 | tells us whether the point lies inside, or outside a 9 | quarter circle running from 0,1 to 1,0. The ratio of the 10 | number of points inside to outside gives us an 11 | approximation of π/4. 12 | 13 | See https://en.wikipedia.org/wiki/File:Pi_30K.gif 14 | for a visualization of how this works. 15 | */ 16 | 17 | inline int randall(unsigned int* p_seed) { 18 | // windows has thread safe rand() 19 | #ifdef _WIN32 20 | return rand(); // NOLINT(runtime/threadsafe_fn) 21 | #else 22 | return rand_r(p_seed); 23 | #endif 24 | } 25 | 26 | double Estimate(int points) { 27 | int i = points; 28 | int inside = 0; 29 | unsigned int randseed = 1; 30 | 31 | #ifdef _WIN32 32 | srand(randseed); 33 | #endif 34 | 35 | // unique seed for each run, for threaded use 36 | unsigned int seed = randall(&randseed); 37 | 38 | #ifdef _WIN32 39 | srand(seed); 40 | #endif 41 | 42 | while (i-- > 0) { 43 | double x = randall(&seed) / static_cast(RAND_MAX); 44 | double y = randall(&seed) / static_cast(RAND_MAX); 45 | 46 | // x & y and now values between 0 and 1 47 | // now do a pythagorean diagonal calculation 48 | // `1` represents our 1/4 circle 49 | if ((x * x) + (y * y) <= 1) inside++; 50 | } 51 | 52 | // calculate ratio and multiply by 4 for π 53 | return (inside / static_cast(points)) * 4; 54 | } 55 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/pi_est.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_ 2 | #define EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_ 3 | 4 | double Estimate(int points); 5 | 6 | #endif // EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_ 7 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/sync.cc: -------------------------------------------------------------------------------- 1 | #include "sync.h" // NOLINT(build/include) 2 | #include 3 | #include "pi_est.h" // NOLINT(build/include) 4 | 5 | // Simple synchronous access to the `Estimate()` function 6 | NAN_METHOD(CalculateSync) { 7 | // expect a number as the first argument 8 | int points = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust(); 9 | double est = Estimate(points); 10 | 11 | info.GetReturnValue().Set(est); 12 | } 13 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/nan/sync.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_ 2 | #define EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_ 3 | 4 | #include 5 | 6 | NAN_METHOD(CalculateSync); 7 | 8 | #endif // EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_ 9 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/README.md: -------------------------------------------------------------------------------- 1 | In this directory run `node-gyp rebuild` and then `node ./addon.js` -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "async.h" // NOLINT(build/include) 3 | #include "sync.h" // NOLINT(build/include) 4 | 5 | // Expose synchronous and asynchronous access to our 6 | // Estimate() function 7 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 8 | exports.Set(Napi::String::New(env, "calculateSync"), 9 | Napi::Function::New(env, CalculateSync)); 10 | exports.Set(Napi::String::New(env, "calculateAsync"), 11 | Napi::Function::New(env, CalculateAsync)); 12 | return exports; 13 | } 14 | 15 | NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) 16 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('addon'); 2 | const calculations = process.argv[2] || 100000000; 3 | 4 | function printResult(type, pi, ms) { 5 | console.log(type, 'method:'); 6 | console.log('\tπ ≈ ' + pi + 7 | ' (' + Math.abs(pi - Math.PI) + ' away from actual)'); 8 | console.log('\tTook ' + ms + 'ms'); 9 | console.log(); 10 | } 11 | 12 | function runSync() { 13 | const start = Date.now(); 14 | // Estimate() will execute in the current thread, 15 | // the next line won't return until it is finished 16 | const result = addon.calculateSync(calculations); 17 | printResult('Sync', result, Date.now() - start); 18 | } 19 | 20 | function runAsync() { 21 | // how many batches should we split the work in to? 22 | const batches = process.argv[3] || 16; 23 | let ended = 0; 24 | let total = 0; 25 | const start = Date.now(); 26 | 27 | function done (err, result) { 28 | total += result; 29 | 30 | // have all the batches finished executing? 31 | if (++ended === batches) { 32 | printResult('Async', total / batches, Date.now() - start); 33 | } 34 | } 35 | 36 | // for each batch of work, request an async Estimate() for 37 | // a portion of the total number of calculations 38 | for (let i = 0; i < batches; i++) { 39 | addon.calculateAsync(calculations / batches, done); 40 | } 41 | } 42 | 43 | runSync(); 44 | runAsync(); 45 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/async.cc: -------------------------------------------------------------------------------- 1 | #include "async.h" // NOLINT(build/include) 2 | #include 3 | #include "pi_est.h" // NOLINT(build/include) 4 | 5 | class PiWorker : public Napi::AsyncWorker { 6 | public: 7 | PiWorker(Napi::Function& callback, int points) 8 | : Napi::AsyncWorker(callback), points(points), estimate(0) {} 9 | ~PiWorker() {} 10 | 11 | // Executed inside the worker-thread. 12 | // It is not safe to access JS engine data structure 13 | // here, so everything we need for input and output 14 | // should go on `this`. 15 | void Execute() { estimate = Estimate(points); } 16 | 17 | // Executed when the async work is complete 18 | // this function will be run inside the main event loop 19 | // so it is safe to use JS engine data again 20 | void OnOK() { 21 | Callback().Call({Env().Undefined(), Napi::Number::New(Env(), estimate)}); 22 | } 23 | 24 | private: 25 | int points; 26 | double estimate; 27 | }; 28 | 29 | // Asynchronous access to the `Estimate()` function 30 | Napi::Value CalculateAsync(const Napi::CallbackInfo& info) { 31 | int points = info[0].As().Uint32Value(); 32 | Napi::Function callback = info[1].As(); 33 | PiWorker* piWorker = new PiWorker(callback, points); 34 | piWorker->Queue(); 35 | return info.Env().Undefined(); 36 | } 37 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/async.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_ 2 | #define EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_ 3 | 4 | #include 5 | 6 | Napi::Value CalculateAsync(const Napi::CallbackInfo& info); 7 | 8 | #endif // EXAMPLES_ASYNC_PI_ESTIMATE_ASYNC_H_ 9 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ 6 | "addon.cc", 7 | "pi_est.cc", 8 | "sync.cc", 9 | "async.cc" 10 | ], 11 | 'cflags!': [ '-fno-exceptions' ], 12 | 'cflags_cc!': [ '-fno-exceptions' ], 13 | 'include_dirs': [" 3 | 4 | /* 5 | Estimate the value of π by using a Monte Carlo method. 6 | Take `points` samples of random x and y values on a 7 | [0,1][0,1] plane. Calculating the length of the diagonal 8 | tells us whether the point lies inside, or outside a 9 | quarter circle running from 0,1 to 1,0. The ratio of the 10 | number of points inside to outside gives us an 11 | approximation of π/4. 12 | 13 | See https://en.wikipedia.org/wiki/File:Pi_30K.gif 14 | for a visualization of how this works. 15 | */ 16 | 17 | inline int randall(unsigned int* p_seed) { 18 | // windows has thread safe rand() 19 | #ifdef _WIN32 20 | return rand(); // NOLINT(runtime/threadsafe_fn) 21 | #else 22 | return rand_r(p_seed); 23 | #endif 24 | } 25 | 26 | double Estimate(int points) { 27 | int i = points; 28 | int inside = 0; 29 | unsigned int randseed = 1; 30 | 31 | #ifdef _WIN32 32 | srand(randseed); 33 | #endif 34 | 35 | // unique seed for each run, for threaded use 36 | unsigned int seed = randall(&randseed); 37 | 38 | #ifdef _WIN32 39 | srand(seed); 40 | #endif 41 | 42 | while (i-- > 0) { 43 | double x = randall(&seed) / static_cast(RAND_MAX); 44 | double y = randall(&seed) / static_cast(RAND_MAX); 45 | 46 | // x & y and now values between 0 and 1 47 | // now do a pythagorean diagonal calculation 48 | // `1` represents our 1/4 circle 49 | if ((x * x) + (y * y) <= 1) inside++; 50 | } 51 | 52 | // calculate ratio and multiply by 4 for π 53 | return (inside / static_cast(points)) * 4; 54 | } 55 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/pi_est.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_ 2 | #define EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_ 3 | 4 | double Estimate(int points); 5 | 6 | #endif // EXAMPLES_ASYNC_PI_ESTIMATE_PI_EST_H_ 7 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/sync.cc: -------------------------------------------------------------------------------- 1 | #include "sync.h" // NOLINT(build/include) 2 | #include 3 | #include "pi_est.h" // NOLINT(build/include) 4 | 5 | // Simple synchronous access to the `Estimate()` function 6 | Napi::Value CalculateSync(const Napi::CallbackInfo& info) { 7 | // expect a number as the first argument 8 | int points = info[0].As().Uint32Value(); 9 | double est = Estimate(points); 10 | 11 | return Napi::Number::New(info.Env(), est); 12 | } 13 | -------------------------------------------------------------------------------- /src/5-async-work/async_pi_estimate/node-addon-api/sync.h: -------------------------------------------------------------------------------- 1 | #ifndef EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_ 2 | #define EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_ 3 | 4 | #include 5 | 6 | Napi::Value CalculateSync(const Napi::CallbackInfo& info); 7 | 8 | #endif // EXAMPLES_ASYNC_PI_ESTIMATE_SYNC_H_ 9 | -------------------------------------------------------------------------------- /src/5-async-work/async_work_promise/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'binding', 5 | 'sources': [ 'binding.c' ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/5-async-work/async_work_promise/napi/index.js: -------------------------------------------------------------------------------- 1 | // Use the "bindings" package to locate the native bindings. 2 | const binding = require('bindings')('binding'); 3 | 4 | // Call the function "startWork" which the native bindings library exposes. 5 | // The function returns a promise which will be resolved at the complete of the 6 | // work with an array of worked out primes. This resolution simply prints them out. 7 | binding.startWork() 8 | .then((thePrimes) => { 9 | console.log("Received primes from completed work: " + thePrimes) 10 | }); 11 | -------------------------------------------------------------------------------- /src/5-async-work/async_work_promise/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async_work_promise", 3 | "version": "0.0.0", 4 | "description": "Calling into JS from the thread pool", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0" 9 | }, 10 | "engines": { 11 | "node": ">= 10.6.0" 12 | }, 13 | "scripts": { 14 | "test": "node index.js" 15 | }, 16 | "gypfile": true 17 | } 18 | -------------------------------------------------------------------------------- /src/5-async-work/async_work_promise/node-addon-api/addon.cc: -------------------------------------------------------------------------------- 1 | #include "napi.h" 2 | #include "worker.h" 3 | 4 | Napi::Value DoHeavyMath(const Napi::CallbackInfo& info) { 5 | Napi::Env env = info.Env(); 6 | 7 | if (!info[0].IsNumber()) { 8 | Napi::TypeError::New(env, "num1 must be a number") 9 | .ThrowAsJavaScriptException(); 10 | return env.Undefined(); 11 | } 12 | uint32_t num_1 = info[0].As().Uint32Value(); 13 | 14 | if (!info[1].IsNumber()) { 15 | Napi::TypeError::New(env, "num2 must be a number") 16 | .ThrowAsJavaScriptException(); 17 | return env.Undefined(); 18 | } 19 | uint32_t num_2 = info[1].As().Uint32Value(); 20 | 21 | DoHeavyMathWorker* worker = new DoHeavyMathWorker(env, num_1, num_2); 22 | worker->Queue(); 23 | return worker->GetPromise(); 24 | } 25 | 26 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 27 | exports.Set(Napi::String::New(env, "doHeavyMath"), 28 | Napi::Function::New(env, DoHeavyMath)); 29 | return exports; 30 | } 31 | 32 | NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) 33 | -------------------------------------------------------------------------------- /src/5-async-work/async_work_promise/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [{ 3 | "target_name": "addon", 4 | "sources": [ 5 | "addon.cc" 6 | ], 7 | "include_dirs": [ 8 | " 8 | console.log("Received prime from secondary thread: " + thePrime)); 9 | -------------------------------------------------------------------------------- /src/5-async-work/async_work_thread_safe_function/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async_work_thread_safe_function", 3 | "version": "0.0.0", 4 | "description": "Calling into JS from the thread pool", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0" 9 | }, 10 | "engines": { 11 | "node": ">= 10.6.0" 12 | }, 13 | "scripts": { 14 | "test": "node index.js" 15 | }, 16 | "gypfile": true 17 | } 18 | -------------------------------------------------------------------------------- /src/5-async-work/call-js-from-async-worker-execute/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "dispatcher", 5 | "sources": [ "src/binding.cc" ], 6 | 'cflags!': [ '-fno-exceptions' ], 7 | 'cflags_cc!': [ '-fno-exceptions' ], 8 | 'include_dirs': [" { 16 | console.log('opened'); 17 | }); 18 | 19 | socket.on('message', (message) => { 20 | console.log(`message: ${message}`); 21 | }); 22 | 23 | socket.on('close', () => { 24 | console.log('closed'); 25 | }); 26 | -------------------------------------------------------------------------------- /src/5-async-work/call-js-from-async-worker-execute/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "call-js-from-async-worker-execute", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons - calls JS from AsyncWorker::Execute", 5 | "main": "index.js", 6 | "private": true, 7 | "gypfile": true, 8 | "scripts": { 9 | "start": "node index.js" 10 | }, 11 | "dependencies": { 12 | "node-addon-api": "*", 13 | "bindings": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/5-async-work/napi-asyncworker-example/node-addon-api/README.md: -------------------------------------------------------------------------------- 1 | # Node-API AsyncWorker Example 2 | 3 | This is an example project showing how to use the Node.js Node-API AsyncWorker class 4 | 5 | A tutorial describing this project can be found at the [Node-API Resource](https://napi.inspiredware.com/special-topics/asyncworker.html). 6 | 7 | To build and run this program on your system, clone it to your computer and run these two commands inside your clone: 8 | 9 | ``` 10 | npm install 11 | npm test 12 | ``` 13 | 14 | > You need to have Node 10.5.0 or later installed. -------------------------------------------------------------------------------- /src/5-async-work/napi-asyncworker-example/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'napi-asyncworker-example-native', 5 | 'sources': [ 'src/RunSimpleAsyncWorker.cc', 'src/SimpleAsyncWorker.cc' ], 6 | 'include_dirs': ["(); 5 | Function callback = info[1].As(); 6 | SimpleAsyncWorker* asyncWorker = new SimpleAsyncWorker(callback, runTime); 7 | asyncWorker->Queue(); 8 | std::string msg = 9 | "SimpleAsyncWorker for " + std::to_string(runTime) + " seconds queued."; 10 | return String::New(info.Env(), msg.c_str()); 11 | }; 12 | 13 | Object Init(Env env, Object exports) { 14 | exports["runSimpleAsyncWorker"] = Function::New( 15 | env, runSimpleAsyncWorker, std::string("runSimpleAsyncWorker")); 16 | return exports; 17 | } 18 | 19 | NODE_API_MODULE(addon, Init) -------------------------------------------------------------------------------- /src/5-async-work/napi-asyncworker-example/node-addon-api/src/SimpleAsyncWorker.cc: -------------------------------------------------------------------------------- 1 | #include "SimpleAsyncWorker.h" 2 | #include 3 | #include 4 | 5 | SimpleAsyncWorker::SimpleAsyncWorker(Function& callback, int runTime) 6 | : AsyncWorker(callback), runTime(runTime){}; 7 | 8 | void SimpleAsyncWorker::Execute() { 9 | std::this_thread::sleep_for(std::chrono::seconds(runTime)); 10 | if (runTime == 4) SetError("Oops! Failed after 'working' 4 seconds."); 11 | }; 12 | 13 | void SimpleAsyncWorker::OnOK() { 14 | std::string msg = "SimpleAsyncWorker returning after 'working' " + 15 | std::to_string(runTime) + " seconds."; 16 | Callback().Call({Env().Null(), String::New(Env(), msg)}); 17 | }; -------------------------------------------------------------------------------- /src/5-async-work/napi-asyncworker-example/node-addon-api/src/SimpleAsyncWorker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | using namespace Napi; 4 | 5 | class SimpleAsyncWorker : public AsyncWorker { 6 | public: 7 | SimpleAsyncWorker(Function& callback, int runTime); 8 | virtual ~SimpleAsyncWorker(){}; 9 | 10 | void Execute(); 11 | void OnOK(); 12 | 13 | private: 14 | int runTime; 15 | }; -------------------------------------------------------------------------------- /src/5-async-work/napi-asyncworker-example/node-addon-api/test/Test.js: -------------------------------------------------------------------------------- 1 | const runWorker = require('../build/Release/napi-asyncworker-example-native'); 2 | 3 | let result = runWorker.runSimpleAsyncWorker(2, AsyncWorkerCompletion); 4 | console.log("runSimpleAsyncWorker returned '"+result+"'."); 5 | 6 | result = runWorker.runSimpleAsyncWorker(4, AsyncWorkerCompletion); 7 | console.log("runSimpleAsyncWorker returned '"+result+"'."); 8 | 9 | result = runWorker.runSimpleAsyncWorker(8, AsyncWorkerCompletion); 10 | console.log("runSimpleAsyncWorker returned '"+result+"'."); 11 | 12 | function AsyncWorkerCompletion (err, result) { 13 | if (err) { 14 | console.log("SimpleAsyncWorker returned an error: ", err); 15 | } else { 16 | console.log("SimpleAsyncWorker returned '"+result+"'."); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/promise-callback-demo/node-addon-api/README.md: -------------------------------------------------------------------------------- 1 | # Node-API Promise Callback Demo 2 | 3 | To build and run this program on your system, clone it to your computer and run these two commands inside your clone: 4 | 5 | ``` 6 | npm install 7 | ``` 8 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/promise-callback-demo/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'promise-callback-demo', 5 | 'sources': [ 'src/promise_callback_demo.cc' ], 6 | 'include_dirs': ["= 10.16.0" 13 | }, 14 | "version": "1.0.0", 15 | "description": "An promise callback demo", 16 | "author": "Node-API Team", 17 | "license": "ISC" 18 | } 19 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/promise-callback-demo/node-addon-api/test/index.js: -------------------------------------------------------------------------------- 1 | const { PromiseCallbackDemo } = require('../build/Release/promise-callback-demo'); 2 | 3 | let shouldFail = false; 4 | // Resolve or reject a promise with an ISO date format after 100 milliseconds 5 | function jsCallback() { 6 | return new Promise((resolve, reject) => setTimeout(() => { 7 | ((shouldFail = !shouldFail) ? reject : resolve)(new Date().toISOString()); 8 | }, 100)); 9 | } 10 | 11 | new PromiseCallbackDemo(jsCallback); 12 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/thread_safe_function_counting/node-addon-api/addon.js: -------------------------------------------------------------------------------- 1 | const { createTSFN } = require('bindings')('addon'); 2 | 3 | const callback = (...args) => { 4 | console.log(new Date, ...args); 5 | }; 6 | 7 | void async function() { 8 | console.log(await createTSFN(callback)); 9 | }(); 10 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/thread_safe_function_counting/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [{ 3 | 'target_name': 'addon', 4 | 'defines': ['V8_DEPRECATION_WARNINGS=1'], 5 | 'sources': ['addon.cc'], 6 | 'include_dirs': ["= 10.16.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/thread_safe_function_round_trip/napi/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'round_trip', 5 | 'sources': [ 'round_trip.c' ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/thread_safe_function_round_trip/napi/index.js: -------------------------------------------------------------------------------- 1 | const bindings = require('bindings')('round_trip'); 2 | 3 | bindings.startThread(item => { 4 | const thePrime = item.prime; 5 | console.log('The prime: ' + thePrime); 6 | 7 | // Answer the call with a 90% probability of returning true somewhere between 8 | // 200 and 400 ms from now. 9 | setTimeout(() => { 10 | const theAnswer = (Math.random() > 0.1); 11 | console.log(thePrime + ': answering with ' + theAnswer); 12 | bindings.registerReturnValue(item, theAnswer); 13 | }, Math.random() * 200 + 200); 14 | }); 15 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/thread_safe_function_round_trip/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "thread_safe_function_round_trip", 3 | "version": "0.0.0", 4 | "description": "Thread-safe Function Example With JavaScript Round Trip", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0" 9 | }, 10 | "engines": { 11 | "node": ">= 10.6.0" 12 | }, 13 | "scripts": { 14 | "test": "node index.js" 15 | }, 16 | "gypfile": true 17 | } 18 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/thread_safe_function_with_object_wrap/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "tsfn_object_wrap", 5 | "sources": [ 6 | "tsfn_object_wrap.cc", 7 | ], 8 | "defines": [ 9 | "NAPI_DISABLE_CPP_EXCEPTIONS", 10 | "NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS" 11 | ], 12 | "include_dirs": [ 13 | " { 3 | // Do something, anything, with x to keep it in scope, otherwise the instance 4 | // will be collected and the process will exit. 5 | x.someProperty = value; 6 | 7 | console.log('JS1: Called with ' + value); 8 | return -value; 9 | }); 10 | 11 | const y = new TsfnObjectWrap((value) => { 12 | // Do something, anything, with y to keep it in scope, otherwise the instance 13 | // will be collected and the process will exit. 14 | y.someProperty = value; 15 | 16 | console.log('JS2: Called with ' + value); 17 | return -value; 18 | }); 19 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/threadsafe-async-iterator/node-addon-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (example) 2 | include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/) 3 | cmake_minimum_required(VERSION 3.18) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | include_directories(${CMAKE_JS_INC}) 8 | file(GLOB SOURCE_FILES "*.cc") 9 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) 10 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") 11 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) 12 | 13 | # Include Node-API wrappers 14 | execute_process(COMMAND node -p "require('node-addon-api').include" 15 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 16 | OUTPUT_VARIABLE NODE_ADDON_API_DIR 17 | ) 18 | string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) 19 | 20 | target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR}) 21 | 22 | # define NAPI_VERSION 23 | add_definitions(-DNAPI_VERSION=6) 24 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/threadsafe-async-iterator/node-addon-api/index.js: -------------------------------------------------------------------------------- 1 | const { ThreadSafeAsyncIteratorExample } = require('bindings')('example'); 2 | 3 | async function main(from, to) { 4 | const iterator = new ThreadSafeAsyncIteratorExample(from, to); 5 | for await (const value of iterator) { 6 | console.log(value); 7 | } 8 | } 9 | 10 | main(0, 5) 11 | .catch(e => { 12 | console.error(e); 13 | process.exit(1); 14 | }); 15 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/threadsafe-async-iterator/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "threadsafe-async-iterator-example", 3 | "version": "0.0.0", 4 | "description": "Async iterator example with threadsafe functions using node-addon-api", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "^1.5.0", 9 | "cmake-js": "^7.3.0", 10 | "node-addon-api": "^8.1.0" 11 | }, 12 | "scripts": { 13 | "test": "node index.js", 14 | "install": "cmake-js compile" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (clock) 2 | include_directories(${CMAKE_JS_INC} node_modules/node-addon-api/) 3 | cmake_minimum_required(VERSION 3.18) 4 | 5 | set(CMAKE_CXX_STANDARD 20) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | include_directories(${CMAKE_JS_INC}) 8 | file(GLOB SOURCE_FILES "*.cc") 9 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) 10 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") 11 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) 12 | 13 | # Include Node-API wrappers 14 | execute_process(COMMAND node -p "require('node-addon-api').include" 15 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 16 | OUTPUT_VARIABLE NODE_ADDON_API_DIR 17 | ) 18 | string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) 19 | 20 | target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR}) 21 | 22 | # define NAPI_VERSION 23 | add_definitions(-DNAPI_VERSION=4) 24 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/index.js: -------------------------------------------------------------------------------- 1 | const { start } = require('bindings')('clock'); 2 | 3 | start.call(new Date(), function (clock) { 4 | const context = this; 5 | console.log(context, clock); 6 | }, 5); 7 | -------------------------------------------------------------------------------- /src/6-threadsafe-function/typed_threadsafe_function/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-typedthreadsafefunction", 3 | "version": "0.0.0", 4 | "description": "node-addon-api TypedThreadSafeFunction example", 5 | "main": "index.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0", 9 | "cmake-js": "^7.3.0", 10 | "node-addon-api": "^8.1.0" 11 | }, 12 | "scripts": { 13 | "test": "node index.js", 14 | "install": "cmake-js compile" 15 | }, 16 | "gypfile": true 17 | } 18 | -------------------------------------------------------------------------------- /src/7-events/emit_event_from_cpp/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "emit_from_cpp", 5 | "sources": [ 6 | "src/emit-from-cpp.cc" 7 | ], 8 | 'cflags!': [ '-fno-exceptions' ], 9 | 'cflags_cc!': [ '-fno-exceptions' ], 10 | 'include_dirs': [" { 9 | console.log('### START ...') 10 | }) 11 | emitter.on('data', (evt) => { 12 | console.log(evt); 13 | }) 14 | 15 | emitter.on('end', () => { 16 | console.log('### END ###') 17 | }) 18 | 19 | addon.callEmit(emitter.emit.bind(emitter)) 20 | -------------------------------------------------------------------------------- /src/7-events/emit_event_from_cpp/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emit-event-from-cpp-example", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons - emit event from C++ to JS", 5 | "main": "index.js", 6 | "private": true, 7 | "gypfile": true, 8 | "scripts": { 9 | "start": "node index.js" 10 | }, 11 | "dependencies": { 12 | "node-addon-api": "*", 13 | "bindings": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/7-events/emit_event_from_cpp/node-addon-api/src/emit-from-cpp.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | Napi::Value CallEmit(const Napi::CallbackInfo& info) { 8 | Napi::Env env = info.Env(); 9 | Napi::Function emit = info[0].As(); 10 | emit.Call({Napi::String::New(env, "start")}); 11 | for (int i = 0; i < 3; i++) { 12 | std::this_thread::sleep_for(std::chrono::seconds(3)); 13 | emit.Call( 14 | {Napi::String::New(env, "data"), Napi::String::New(env, "data ...")}); 15 | } 16 | emit.Call({Napi::String::New(env, "end")}); 17 | return Napi::String::New(env, "OK"); 18 | } 19 | 20 | // Init 21 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 22 | exports.Set(Napi::String::New(env, "callEmit"), 23 | Napi::Function::New(env, CallEmit)); 24 | return exports; 25 | } 26 | 27 | NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init); 28 | -------------------------------------------------------------------------------- /src/7-events/inherits_from_event_emitter/node-addon-api/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "native_emitter", 5 | "sources": [ 6 | "src/binding.cc", 7 | "src/native-emitter.cc" 8 | ], 9 | 'cflags!': [ '-fno-exceptions' ], 10 | 'cflags_cc!': [ '-fno-exceptions' ], 11 | 'include_dirs': [" { 12 | console.log('### START ...') 13 | }) 14 | 15 | emitter.on('data', (evt) => { 16 | console.log(evt) 17 | }) 18 | 19 | emitter.on('end', () => { 20 | console.log('### END ###') 21 | }) 22 | 23 | emitter.callAndEmit() 24 | -------------------------------------------------------------------------------- /src/7-events/inherits_from_event_emitter/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inherits-from-event-emitter-example", 3 | "version": "0.0.0", 4 | "description": "Node.js Addons - inherits from event emitter", 5 | "main": "index.js", 6 | "private": true, 7 | "gypfile": true, 8 | "scripts": { 9 | "start": "node index.js" 10 | }, 11 | "dependencies": { 12 | "node-addon-api": "*", 13 | "bindings": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/7-events/inherits_from_event_emitter/node-addon-api/src/binding.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "native-emitter.h" 3 | 4 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 5 | NativeEmitter::Init(env, exports); 6 | return exports; 7 | } 8 | 9 | NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) 10 | -------------------------------------------------------------------------------- /src/7-events/inherits_from_event_emitter/node-addon-api/src/native-emitter.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "native-emitter.h" 6 | 7 | Napi::FunctionReference NativeEmitter::constructor; 8 | 9 | Napi::Object NativeEmitter::Init(Napi::Env env, Napi::Object exports) { 10 | Napi::Function func = 11 | DefineClass(env, 12 | "NativeEmitter", 13 | {InstanceMethod("callAndEmit", &NativeEmitter::CallAndEmit)}); 14 | 15 | constructor = Napi::Persistent(func); 16 | constructor.SuppressDestruct(); 17 | 18 | exports.Set("NativeEmitter", func); 19 | return exports; 20 | } 21 | 22 | NativeEmitter::NativeEmitter(const Napi::CallbackInfo& info) 23 | : Napi::ObjectWrap(info) { 24 | // NOOP 25 | } 26 | 27 | Napi::Value NativeEmitter::CallAndEmit(const Napi::CallbackInfo& info) { 28 | Napi::Env env = info.Env(); 29 | Napi::Function emit = 30 | info.This().As().Get("emit").As(); 31 | emit.Call(info.This(), {Napi::String::New(env, "start")}); 32 | for (int i = 0; i < 3; i++) { 33 | std::this_thread::sleep_for(std::chrono::seconds(1)); 34 | emit.Call( 35 | info.This(), 36 | {Napi::String::New(env, "data"), Napi::String::New(env, "data ...")}); 37 | } 38 | emit.Call(info.This(), {Napi::String::New(env, "end")}); 39 | return Napi::String::New(env, "OK"); 40 | } 41 | -------------------------------------------------------------------------------- /src/7-events/inherits_from_event_emitter/node-addon-api/src/native-emitter.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class NativeEmitter : public Napi::ObjectWrap { 4 | public: 5 | static Napi::Object Init(Napi::Env env, Napi::Object exports); 6 | NativeEmitter(const Napi::CallbackInfo& info); 7 | 8 | private: 9 | static Napi::FunctionReference constructor; 10 | 11 | Napi::Value CallAndEmit(const Napi::CallbackInfo& info); 12 | }; 13 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/README.md: -------------------------------------------------------------------------------- 1 | ## Building Node-API Addons Using CMake.js 2 | 3 | ### Examples 4 | 5 | The objective of these examples is to demonstrate how to build Node-API addons using [CMake.js](https://github.com/cmake-js/cmake-js#readme). 6 | 7 | These example projects assume that CMake.js has been installed globally: 8 | 9 | ``` 10 | npm install -g cmake-js 11 | cmake-js --help 12 | ``` 13 | 14 | Then, in each of the `napi` and `node-addon-api` directories, the following commands build and test each addon: 15 | 16 | ``` 17 | npm install 18 | npm test 19 | ``` 20 | 21 | Complete CMake.js documentation can be found on the [CMake.js GitHub repository](https://github.com/cmake-js/cmake-js#readme). 22 | 23 | ### NAPI_VERSION 24 | 25 | When building Node-API addons, it's important to specify to the build system the Node-API version your code is designed to work with. With CMake.js, this information is specified in the `CMakeLists.txt` file: 26 | 27 | ``` 28 | add_definitions(-DNAPI_VERSION=3) 29 | ``` 30 | 31 | Since Node-API is ABI-stable, your Node-API addon will work, without recompilation, with the Node-API version you specify in `NAPI_VERSION` and all subsequent Node-API versions. 32 | 33 | In the absence of a need for features available only in a specific Node-API version, version 3 is a good choice as it is the version of Node-API that was active when Node-API left experimental status. -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/napi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | cmake_policy(SET CMP0042 NEW) 3 | set (CMAKE_CXX_STANDARD 11) 4 | 5 | project (build-napi-with-cmake) 6 | include_directories(${CMAKE_JS_INC}) 7 | file(GLOB SOURCE_FILES "hello.c") 8 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) 9 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") 10 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) 11 | 12 | # Define NAPI_VERSION 13 | add_definitions(-DNAPI_VERSION=3) 14 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/napi/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static napi_value Method(napi_env env, napi_callback_info info) { 5 | napi_status status; 6 | napi_value world; 7 | status = napi_create_string_utf8(env, "Hello, world!", 13, &world); 8 | assert(status == napi_ok); 9 | return world; 10 | } 11 | 12 | #define DECLARE_NAPI_METHOD(name, func) \ 13 | { name, 0, func, 0, 0, 0, napi_default, 0 } 14 | 15 | static napi_value Init(napi_env env, napi_value exports) { 16 | napi_status status; 17 | napi_property_descriptor desc = DECLARE_NAPI_METHOD("hello", Method); 18 | status = napi_define_properties(env, exports, 1, &desc); 19 | assert(status == napi_ok); 20 | return exports; 21 | } 22 | 23 | NAPI_MODULE(hello, Init) 24 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/napi/hello.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('build-napi-with-cmake'); 2 | 3 | console.log(addon.hello()); // 'world' 4 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/napi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "build-napi-with-cmake", 3 | "version": "0.0.0", 4 | "description": "Build Node-API native addon with CMake.", 5 | "main": "hello.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0" 9 | }, 10 | "scripts": { 11 | "install": "cmake-js compile", 12 | "test": "node hello.js" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/node-addon-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | cmake_policy(SET CMP0042 NEW) 3 | set (CMAKE_CXX_STANDARD 11) 4 | 5 | project (build-node-addon-api-with-cmake) 6 | include_directories(${CMAKE_JS_INC}) 7 | file(GLOB SOURCE_FILES "hello.cc") 8 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) 9 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") 10 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) 11 | 12 | # Include Node-API wrappers 13 | execute_process(COMMAND node -p "require('node-addon-api').include" 14 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 15 | OUTPUT_VARIABLE NODE_ADDON_API_DIR 16 | ) 17 | string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) 18 | 19 | target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR}) 20 | 21 | # define NAPI_VERSION 22 | add_definitions(-DNAPI_VERSION=3) 23 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/node-addon-api/hello.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static Napi::String Method(const Napi::CallbackInfo& info) { 4 | // Napi::Env is the opaque data structure containing the environment in which 5 | // the request is being run. We will need this env when we want to create any 6 | // new objects inside of the node.js environment 7 | Napi::Env env = info.Env(); 8 | 9 | // Create a C++ level variable 10 | std::string helloWorld = "Hello, world!"; 11 | 12 | // Return a new javascript string that we copy-construct inside of the node.js 13 | // environment 14 | return Napi::String::New(env, helloWorld); 15 | } 16 | 17 | static Napi::Object Init(Napi::Env env, Napi::Object exports) { 18 | exports.Set(Napi::String::New(env, "hello"), 19 | Napi::Function::New(env, Method)); 20 | return exports; 21 | } 22 | 23 | NODE_API_MODULE(hello, Init) 24 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/node-addon-api/hello.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('build-node-addon-api-with-cmake'); 2 | 3 | console.log(addon.hello()); // 'world' 4 | -------------------------------------------------------------------------------- /src/8-tooling/build_with_cmake/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "build-node-addon-api-with-cmake", 3 | "version": "0.0.0", 4 | "description": "Build Node-API native addon with CMake and node-addon-api C++ wrapper.", 5 | "main": "hello.js", 6 | "private": true, 7 | "dependencies": { 8 | "bindings": "~1.5.0", 9 | "node-addon-api": "^8.1.0" 10 | }, 11 | "scripts": { 12 | "install": "cmake-js compile", 13 | "test": "node hello.js" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | set (CMAKE_CXX_STANDARD 11) 3 | 4 | project(typescript_with_addon VERSION 0.0.1) 5 | include_directories(${CMAKE_JS_INC}) 6 | string(APPEND CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wendif-labels") 7 | 8 | file(GLOB SOURCE_FILES *.cpp *.h *.cc *.c) 9 | add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) 10 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") 11 | target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) 12 | 13 | # Include Node-API wrappers 14 | execute_process(COMMAND node -p "require('node-addon-api').include" 15 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 16 | OUTPUT_VARIABLE NODE_ADDON_API_DIR 17 | ) 18 | 19 | # need to pay attention to this part 20 | string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR}) 21 | 22 | target_include_directories(${PROJECT_NAME} PRIVATE ${NODE_ADDON_API_DIR}) 23 | 24 | # Define NAPI_VERSION 25 | add_definitions(-DNAPI_VERSION=3) 26 | 27 | -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/README.md: -------------------------------------------------------------------------------- 1 | ## TypeScript calls JavaScript that calls C++ that calls C function 2 | 3 | ### Build and run: 4 | 5 | ``` 6 | npm install 7 | npm test 8 | ``` 9 | 10 | The result: 11 | 12 | ``` 13 | This is a TypeScript class constructor. 14 | This is a Javascript function. 15 | This is a C++ function. 16 | This is a C function. 17 | ``` 18 | 19 | This is using cmake and cmake-js based on the example [build_with_cmake](../../build_with_cmake) 20 | -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/cPart.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cPart.h" 3 | 4 | int cPart(int value) { 5 | int newValue = value + 30; 6 | printf("I'm a C function and I received %d from C++ and I'm sending back %d \n",value,newValue); 7 | return newValue; 8 | } -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/cPart.h: -------------------------------------------------------------------------------- 1 | #ifndef _C_PART_H_ 2 | #define _C_PART_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | int cPart(int); 9 | 10 | #ifdef __cplusplus 11 | } // extern "C" 12 | #endif 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/cppPart.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cPart.h" 3 | 4 | using namespace std; 5 | 6 | int callingCPart(int value) { 7 | int result = cPart(++value); 8 | cout << "I'm a C++ function I'm sending " << value << " to C " 9 | << "and I received " << result << endl; 10 | return result; 11 | } -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/index.ts: -------------------------------------------------------------------------------- 1 | import {javascriptPart} from './jsPart'; 2 | 3 | class TypeScriptPart { 4 | constructor(value:number) { 5 | console.log('I\'m a TypeScript class constructor I\'m sending', 6 | value, 7 | 'to JavaScript and I received', javascriptPart(value)); 8 | } 9 | } 10 | 11 | new TypeScriptPart(10); -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/jsPart.js: -------------------------------------------------------------------------------- 1 | const addon = require('bindings')('typescript_with_addon'); 2 | 3 | function javascriptPart (value) { 4 | const result = addon.cppPartExportedByNapi(++value); 5 | 6 | console.log('I\'m a Javascript function and I\'m sending', 7 | value, 8 | 'to C++ and I received', 9 | result); 10 | 11 | return result; 12 | } 13 | 14 | module.exports = { 15 | javascriptPart 16 | } -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/napiPart.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int callingCPart(int value); 4 | 5 | Napi::Value Method(const Napi::CallbackInfo& info) { 6 | Napi::Env env = info.Env(); 7 | int value = info[0].As().Int32Value(); 8 | Napi::Number num = Napi::Number::New(env, callingCPart(value)); 9 | return num; 10 | } 11 | 12 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 13 | exports.Set(Napi::String::New(env, "cppPartExportedByNapi"), 14 | Napi::Function::New(env, Method)); 15 | return exports; 16 | } 17 | 18 | NODE_API_MODULE(cppPartExportedByNapi, Init) -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript_with_addon", 3 | "version": "0.0.0", 4 | "description": "TypeScript calling Javascript calling C++ calling C with building with CMake and node-addon-api C++ wrapper.", 5 | "dependencies": { 6 | "bindings": "^1.5.0", 7 | "node-addon-api": "^8.1.0" 8 | }, 9 | "scripts": { 10 | "install": "npx cmake-js compile", 11 | "test": "ts-node index.ts" 12 | }, 13 | "devDependencies": { 14 | "ts-node": "^10.9.2", 15 | "typescript": "^5.2.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/8-tooling/typescript_with_addon/node-addon-api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "commonjs" 5 | } 6 | } 7 | 8 | -------------------------------------------------------------------------------- /website/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.py] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [*.rb] 17 | indent_style = space 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /website/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # These files are text and should be normalized 5 | *.bat text eol=crlf 6 | *.coffee text 7 | *.css text 8 | *.htm text 9 | *.html text 10 | *.inc text 11 | *.ini text 12 | *.js text 13 | *.json text 14 | *.jsx text 15 | *.less text 16 | *.sass text 17 | *.scss text 18 | *.sh text eol=lf 19 | *.sql text 20 | *.ts text 21 | *.tsx text 22 | *.xml text 23 | *.xhtml text 24 | 25 | # These files are binary and should be left untouched 26 | # (binary is a macro for -text -diff) 27 | *.png binary 28 | *.jpg binary 29 | *.jpeg binary 30 | *.gif binary 31 | *.ico binary 32 | *.mov binary 33 | *.mp4 binary 34 | *.mp3 binary 35 | *.flv binary 36 | *.fla binary 37 | *.swf binary 38 | *.gz binary 39 | *.zip binary 40 | *.7z binary 41 | *.ttf binary 42 | *.eot binary 43 | *.otf binary 44 | *.woff binary 45 | *.woff2 binary 46 | 47 | # Custom for Visual Studio 48 | *.cs diff=csharp 49 | *.sln merge=union 50 | *.csproj merge=union 51 | *.vbproj merge=union 52 | *.fsproj merge=union 53 | *.dbproj merge=union 54 | 55 | # Standard to msysgit 56 | *.doc diff=astextplain 57 | *.DOC diff=astextplain 58 | *.docx diff=astextplain 59 | *.DOCX diff=astextplain 60 | *.dot diff=astextplain 61 | *.DOT diff=astextplain 62 | *.pdf diff=astextplain 63 | *.PDF diff=astextplain 64 | *.rtf diff=astextplain 65 | *.RTF diff=astextplain 66 | -------------------------------------------------------------------------------- /website/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Kata.ai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /website/docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs/node-addon-examples/4213d4c9d07996ae68629c67926251e117f8e52a/website/docs/.gitkeep -------------------------------------------------------------------------------- /website/docs/about/uses.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: about.uses 3 | title: Uses for Node-API 4 | prev: about.what 5 | --- 6 | 7 | ## An existing C/C++ library 8 | 9 | Perhaps the most popular use for Node-API is making the capabilities of an existing C/C++ library available to JavaScript programmers. This permits you to leverage the investment you've already made in your existing code, making it available to a whole new population of JavaScript programmers and projects. 10 | 11 | For many applications, the C/C++ code base is the reference implementation. Node-API permits you to continue to refine and maintain your existing C/C++ code. Improvements to the C/C++ code are then easily transferred to the JavaScript users. 12 | 13 | ## Access to OS resources 14 | 15 | Some applications, like those built on [Electron](https://electronjs.org) or [NW.js](https://nwjs.io), can benefit from accessing system toolkits and APIs not currently available through Node. Node-API facilities accessing these system resources. 16 | 17 | ## Computational tasks 18 | 19 | For low-powered devices, it may make sense to code computationally intensive tasks in C or C++ and then make that code available to JavaScript using Node-API. Although in many cases Node's JavaScript runtime engine will eventually compile the JavaScript down to binary, Node-API offers the ability to compile the C/C++ code just once and have the binary available to Node right from the start. -------------------------------------------------------------------------------- /website/docs/build-tools/node-gyp.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: build-tools.node-gyp 3 | title: node-gyp 4 | next: build-tools.cmake-js 5 | --- 6 | 7 | Historically, [node-gyp](https://github.com/nodejs/node-gyp) has been the build tool of choice for the Node ecosystem. The tool comes bundled with Node and is nearly universally used to build Node native modules. Most of the examples on this site use node-gyp to build the binaries. 8 | 9 | node-gyp is based upon Google's [GYP](https://gyp.gsrc.io/) build tool. GYP, in turn, requires Python. 10 | 11 | > node-gyp requires Python 2.7 or Python 3.5+ depending upon the operating system on which the native module is being built. The specifics of the requirements can be found [here](https://github.com/nodejs/node-gyp#installation). 12 | 13 | For developers who find the node-gyp build tool too constraining, [CMake.js](cmake-js) is a good alternative. 14 | 15 | ### Pros 16 | 17 | - Comes bundled with Node. 18 | - Is nearly universally used by the Node community to build native modules. 19 | 20 | ### Cons 21 | 22 | - The underlying GYP tool is no longer in active development by Google. 23 | - Some developers find node-gyp too limited for their projects. 24 | -------------------------------------------------------------------------------- /website/docs/getting-started/prerequisites.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: getting-started.prerequisites 3 | title: Prerequisites 4 | next: getting-started.tools 5 | --- 6 | 7 | ## C/C++ and JavaScript 8 | 9 | The purpose of Node-API is to enable you to use your existing or newly written C/C++ code from JavaScript. This assumes at least a passing familiarity with both JavaScript and C/C++. The level of familiarity necessary is dependent upon the complexity of your project and your involvement with the JavaScript and the C/C++. 10 | 11 | For your own projects, it will be necessary to have an understanding of the C/C++ code you plan to integrate as well as what objectives you hope to achieve with your project on the JavaScript side of things. 12 | 13 | ## Command line tools 14 | 15 | Many of the tools used to develop Node-API modules are run from the command line. So at least a passing familiarity and confidence with your command line tool is essential. IDEs, as described [here](tools#other-tools) can help. But they also rely in the command line. 16 | -------------------------------------------------------------------------------- /website/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | id: index 4 | title: Welcome to the Node-API Resource 5 | --- 6 | 7 | The goal of this site is to be a clearinghouse for everything related [Node's](https://nodejs.org/en/about/) ABI-Stable C/C++ API, [Node-API](https://nodejs.org/api/n-api.html#n_api_node_api). 8 | 9 | If you are looking to make existing C/C++ code accessible to the widening universe of JavaScript projects, or if you have need to access operating system's resources from JavaScript, or if you have a particularly computationally intensive task that would benefit from hand-tuned C/C++ code accessible from JavaScript, Node-API may be a good fit for you. 10 | 11 | This site is maintained by members of the Node.js Node-API team. Please let us know if there is anything we can do to answer your questions or make this site better. We welcome your feedback through the link available at the bottom of each page. 12 | 13 | ## Contributors 14 | 15 | | Name | GitHub Link | 16 | | ---- | ----------- | 17 | | Gabriel Schulhof | [gabrielschulhof](https://github.com/gabrielschulhof) | 18 | | Nicola Del Gobbo | [NickNaso](https://github.com/NickNaso) | 19 | | Jim Schlight | [jschlight](https://github.com/jschlight) | 20 | | Michael Dawson | [mhdawson](https://github.com/mhdawson) | 21 | | Kevin Eady | [KevinEady](https://github.com/KevinEady) | 22 | -------------------------------------------------------------------------------- /website/docs/menu.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "getting-started", 4 | "label": "About Node-API", 5 | "href": "/about/what", 6 | "external": false 7 | }, 8 | { 9 | "id": "n-api", 10 | "label": "Node-API Documentation", 11 | "href": "https://nodejs.org/api/n-api.html#n_api_n_api", 12 | "external": true 13 | }, 14 | { 15 | "id": "node-addon-api", 16 | "label": "node-addon-api Module", 17 | "href": "https://github.com/nodejs/node-addon-api", 18 | "external": true 19 | }, 20 | { 21 | "id": "examples", 22 | "label": "Examples", 23 | "href": "https://github.com/nodejs/node-addon-examples", 24 | "external": true 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /website/src/components/docs/DocsHeader/DocsHeader.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Heading, Box, Text } from 'components/foundations'; 3 | 4 | interface DocsHeaderProps { 5 | title: string; 6 | subtitle?: string; 7 | } 8 | 9 | const DocsHeader: React.FC = ({ title, subtitle }) => { 10 | return ( 11 | 12 | 13 | {title} 14 | 15 | {subtitle && ( 16 | 17 | {subtitle} 18 | 19 | )} 20 | 21 | ); 22 | }; 23 | 24 | export default DocsHeader; 25 | -------------------------------------------------------------------------------- /website/src/components/docs/DocsHeader/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DocsHeader } from './DocsHeader'; 2 | -------------------------------------------------------------------------------- /website/src/components/docs/DocsWrapper/DocsWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { breakpoints } from 'components/foundations/variables'; 2 | import styled from 'styled-components'; 3 | 4 | interface DocsWrapperProps { 5 | hasToc?: boolean; 6 | } 7 | 8 | const DocsWrapper = styled('article')` 9 | display: flex; 10 | flex-direction: column; 11 | flex: 1 1 auto; 12 | position: relative; 13 | padding: 32px; 14 | 15 | @media (min-width: ${breakpoints.lg}px) { 16 | flex-direction: ${props => props.hasToc && 'row-reverse'}; 17 | } 18 | 19 | @media (max-width: ${breakpoints.lg - 1}px) { 20 | overflow-x: auto; 21 | } 22 | `; 23 | 24 | export default DocsWrapper; 25 | -------------------------------------------------------------------------------- /website/src/components/docs/DocsWrapper/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DocsWrapper } from './DocsWrapper'; 2 | -------------------------------------------------------------------------------- /website/src/components/docs/TableOfContents/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TocWrapper } from './TocWrapper'; 2 | export { default as TocFloatingButton } from './TocFloatingButton'; 3 | -------------------------------------------------------------------------------- /website/src/components/foundations/Theme.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ThemeProvider } from 'styled-components'; 3 | import { breakpoints, headingSizes, textSizes, colors, space, fonts, paragraphSizes } from './variables'; 4 | 5 | export const themeProps = { 6 | colors, 7 | space, 8 | fonts, 9 | breakpoints: [`${breakpoints.sm}px`, `${breakpoints.md}px`, `${breakpoints.lg}px`, `${breakpoints.xl}px`], 10 | typeScale: { 11 | heading: headingSizes, 12 | paragraph: paragraphSizes, 13 | text: textSizes 14 | } 15 | }; 16 | 17 | export const Theme = (props: { children: React.ReactNode }) => { 18 | return ( 19 | 20 | <>{props.children} 21 | 22 | ); 23 | }; 24 | 25 | export type TypeScale = typeof themeProps.typeScale; 26 | export type HeadingSizes = typeof headingSizes; 27 | export type TextSizes = typeof textSizes; 28 | export type Color = keyof typeof themeProps['colors']; 29 | export type Space = keyof typeof themeProps['space']; 30 | -------------------------------------------------------------------------------- /website/src/components/foundations/box/components/BorderBox.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { 3 | BackgroundProps, 4 | HeightProps, 5 | MaxWidthProps, 6 | SpaceProps, 7 | borderRadius, 8 | BorderRadiusProps, 9 | WidthProps 10 | } from 'styled-system'; 11 | 12 | import { getColor } from 'utils/helpers'; 13 | import { Color } from 'components/foundations'; 14 | 15 | import { Box, BoxProps } from './Box'; 16 | 17 | export interface BorderBoxProps 18 | extends BackgroundProps, 19 | HeightProps, 20 | MaxWidthProps, 21 | SpaceProps, 22 | BorderRadiusProps, 23 | WidthProps, 24 | BoxProps { 25 | /** Set to `true` to enable `overflow: hidden;`. */ 26 | noOverflow?: boolean; 27 | /** The color key for the border. */ 28 | borderColor?: Color; 29 | } 30 | 31 | /** 32 | * An extended `Box` with additional hooks to set border. 33 | */ 34 | export const BorderBox = styled(Box)` 35 | border: 1px solid ${props => getColor(props.borderColor ? props.borderColor : 'grey02')}; 36 | ${borderRadius}; 37 | ${props => props.noOverflow && 'overflow: hidden;'} 38 | `; 39 | 40 | BorderBox.displayName = 'BorderBox'; 41 | -------------------------------------------------------------------------------- /website/src/components/foundations/box/components/Box.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | import { 4 | layout, 5 | LayoutProps, 6 | position, 7 | PositionProps, 8 | flexbox, 9 | FlexboxProps, 10 | grid, 11 | GridProps, 12 | space, 13 | SpaceProps, 14 | background, 15 | BackgroundProps, 16 | color, 17 | ColorProps, 18 | typography, 19 | TypographyProps 20 | } from 'styled-system'; 21 | 22 | export interface BoxProps 23 | extends LayoutProps, 24 | PositionProps, 25 | FlexboxProps, 26 | GridProps, 27 | SpaceProps, 28 | BackgroundProps, 29 | ColorProps, 30 | TypographyProps { 31 | /** Additional CSS classes to add to the component. */ 32 | className?: string; 33 | /** Additional CSS properties to add to the component. */ 34 | style?: React.CSSProperties; 35 | } 36 | 37 | /** 38 | * Box is a view with all styled-system hooks added to it. You can use it as a 39 | * base component for all display elements. 40 | */ 41 | export const Box = styled('div')` 42 | ${layout} 43 | ${position} 44 | ${flexbox} 45 | ${grid} 46 | ${space} 47 | ${background} 48 | ${color} 49 | ${typography} 50 | `; 51 | 52 | Box.displayName = 'Box'; 53 | -------------------------------------------------------------------------------- /website/src/components/foundations/box/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Box'; 2 | export * from './BorderBox'; 3 | -------------------------------------------------------------------------------- /website/src/components/foundations/box/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | -------------------------------------------------------------------------------- /website/src/components/foundations/index.ts: -------------------------------------------------------------------------------- 1 | export * from './box'; 2 | export * from './reset'; 3 | export * from './typography'; 4 | export * from './Theme'; 5 | -------------------------------------------------------------------------------- /website/src/components/foundations/reset/components/GlobalStyles.ts: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components'; 2 | import reboot from '../styles/reboot'; 3 | import base from '../styles/base'; 4 | import code from '../styles/code'; 5 | 6 | const GlobalStyles = createGlobalStyle` 7 | ${reboot} 8 | ${base} 9 | ${code} 10 | `; 11 | 12 | export default GlobalStyles; 13 | -------------------------------------------------------------------------------- /website/src/components/foundations/reset/components/ThemeReset.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { Theme } from '../../Theme'; 4 | import GlobalStyles from './GlobalStyles'; 5 | 6 | interface ResetProps { 7 | className?: string; 8 | style?: React.CSSProperties; 9 | } 10 | 11 | const ThemeReset: React.FC = ({ children }) => { 12 | return ( 13 | 14 | 15 | {children} 16 | 17 | ); 18 | }; 19 | 20 | export default ThemeReset; 21 | -------------------------------------------------------------------------------- /website/src/components/foundations/reset/index.ts: -------------------------------------------------------------------------------- 1 | import GlobalStyles from './components/GlobalStyles'; 2 | import ThemeReset from './components/ThemeReset'; 3 | 4 | export { GlobalStyles, ThemeReset }; 5 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/components/Heading.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { HeadingSizes } from '../../Theme'; 5 | import { determineFontDimensions } from '../utils'; 6 | import { Typography, TypographyProps } from './Typography'; 7 | 8 | export interface HeadingProps extends TypographyProps { 9 | /** Additional CSS classes to add to the component. */ 10 | className?: string; 11 | /** Additional CSS properties to add to the component. */ 12 | style?: React.CSSProperties; 13 | /** What HTML element to render the text as. */ 14 | as?: keyof JSX.IntrinsicElements | React.ComponentType; 15 | /** Size value of the heading. */ 16 | scale?: keyof HeadingSizes; 17 | } 18 | 19 | /** 20 | * This is a base `Text` element to handle typography elements. 21 | */ 22 | const StyledText = styled(Typography)` 23 | ${props => props.scale === 100 && 'text-transform: uppercase;'} 24 | `; 25 | 26 | /** 27 | * Heading component provided as a styled component primitive. 28 | */ 29 | export const Heading: React.SFC = ({ children, as, scale: size, color, ...rest }) => ( 30 | 37 | {children} 38 | 39 | ); 40 | 41 | Heading.defaultProps = { 42 | as: 'h2', 43 | color: 'grey09', 44 | scale: 800 45 | }; 46 | 47 | Heading.displayName = 'Heading'; 48 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/components/Link.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { Omit } from 'utils/types'; 4 | import { determineFontDimensions } from '../utils'; 5 | import { TextProps, Text } from './Text'; 6 | 7 | export interface LinkProps extends React.AnchorHTMLAttributes, Omit {} 8 | 9 | /** 10 | * Link component provided as a styled component primitive. 11 | */ 12 | export const Link: React.SFC = ({ children, scale, ...rest }) => { 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | }; 19 | 20 | Link.displayName = 'Link'; 21 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/components/Paragraph.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { TextSizes } from '../../Theme'; 5 | import { determineFontDimensions } from '../utils'; 6 | import { Typography, TypographyProps } from './Typography'; 7 | 8 | /** 9 | * This is a base `Text` element to handle typography elements. 10 | */ 11 | const StyledText = styled(Typography)` 12 | letter-spacing: -0.05px; 13 | `; 14 | 15 | export interface ParagraphProps extends TypographyProps { 16 | /** Additional CSS classes to add to the component. */ 17 | className?: string; 18 | /** Additional CSS properties to add to the component. */ 19 | style?: React.CSSProperties; 20 | /** What HTML element to render the text as. */ 21 | as?: keyof JSX.IntrinsicElements | React.ComponentType; 22 | /** Size value of the text. */ 23 | scale?: keyof TextSizes; 24 | } 25 | 26 | /** 27 | * Paragraph component provided as a styled component primitive. 28 | */ 29 | export const Paragraph: React.SFC = ({ children, as, scale, ...rest }) => ( 30 | 31 | {children} 32 | 33 | ); 34 | 35 | Paragraph.defaultProps = { 36 | as: 'p', 37 | color: 'grey07', 38 | scale: 300 39 | }; 40 | 41 | Paragraph.displayName = 'Paragraph'; 42 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/components/Text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { TextSizes } from '../../Theme'; 5 | import { determineFontDimensions } from '../utils'; 6 | import { Typography, TypographyProps } from './Typography'; 7 | 8 | /** 9 | * This is a base `Text` element to handle typography elements. 10 | */ 11 | const StyledText = styled(Typography)` 12 | letter-spacing: -0.05px; 13 | `; 14 | 15 | export interface TextProps extends TypographyProps { 16 | /** Additional CSS classes to add to the component. */ 17 | className?: string; 18 | /** Additional CSS properties to add to the component. */ 19 | style?: React.CSSProperties; 20 | /** What HTML element to render the text as. */ 21 | as?: keyof JSX.IntrinsicElements | React.ComponentType; 22 | /** Size value of the text. */ 23 | scale?: keyof TextSizes; 24 | } 25 | 26 | /** 27 | * Text component provided as a styled component primitive. 28 | */ 29 | export const Text: React.SFC = ({ children, as, scale: size, ...rest }) => ( 30 | 31 | {children} 32 | 33 | ); 34 | 35 | Text.defaultProps = { 36 | as: 'span', 37 | scale: 300 38 | }; 39 | 40 | Text.displayName = 'Text'; 41 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/components/Typography.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { layout, LayoutProps, space, SpaceProps, color, ColorProps, typography, TypographyProps } from 'styled-system'; 3 | 4 | export interface TypographyProps extends LayoutProps, SpaceProps, ColorProps, TypographyProps { 5 | /** Extended color props. */ 6 | color?: string; 7 | } 8 | 9 | /** 10 | * This is a base `Text` element to handle typography elements. 11 | */ 12 | export const Typography = styled('span')` 13 | ${layout} 14 | ${space} 15 | ${color} 16 | ${typography} 17 | `; 18 | 19 | Typography.displayName = 'Typography'; 20 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components/Heading'; 2 | export * from './components/Link'; 3 | export * from './components/Paragraph'; 4 | export * from './components/Text'; 5 | 6 | export * from './utils'; 7 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/utils/determineFontDimensions.tsx: -------------------------------------------------------------------------------- 1 | import { themeProps, TypeScale } from '../../Theme'; 2 | 3 | /** 4 | * Determines font sizes based on the text type and size index. 5 | * 6 | * @param textType Either `text` or `heading`. 7 | * @param scale The size key. 8 | */ 9 | export function determineFontDimensions(textType: keyof TypeScale, scale: number = 400) { 10 | const match: any = (themeProps.typeScale[textType] as any)[scale]; 11 | 12 | if (textType === 'heading') { 13 | const styleProps = { 14 | fontSize: `${match.fontSize}px`, 15 | lineHeight: `${match.lineHeight}px`, 16 | fontWeight: scale <= 400 ? 600 : 500, 17 | letterSpacing: `${match.letterSpacing}px` 18 | }; 19 | 20 | return { 21 | ...styleProps, 22 | ...(scale === 100 ? { textTransform: 'uppercase' } : {}) 23 | }; 24 | } 25 | 26 | return { 27 | fontSize: `${match.fontSize}px`, 28 | lineHeight: `${match.lineHeight}px`, 29 | fontWeight: 400 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /website/src/components/foundations/typography/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './determineFontDimensions'; 2 | -------------------------------------------------------------------------------- /website/src/components/layout/Container/Container.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const Container = styled('div')` 4 | position: relative; 5 | margin-left: auto; 6 | margin-right: auto; 7 | width: 100%; 8 | max-width: 704px; 9 | `; 10 | 11 | export default Container; 12 | -------------------------------------------------------------------------------- /website/src/components/layout/Container/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Container } from './Container'; 2 | -------------------------------------------------------------------------------- /website/src/components/layout/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { colors } from 'components/foundations/variables'; 4 | import { Paragraph } from 'components/foundations'; 5 | 6 | const Wrapper = styled('footer')` 7 | padding-top: 24px; 8 | border-top: 1px solid ${colors.grey02}; 9 | `; 10 | 11 | const Footer: React.SFC = () => ( 12 | 13 | 14 |
15 | Comments or suggestions about this page?{' '} 16 | 17 | Let us know 18 | 19 | . 20 |
21 | 22 | Created with{' '} 23 | 24 | Grundgesetz 25 | 26 | . 27 | 28 |
29 |
30 | ); 31 | 32 | export default Footer; 33 | -------------------------------------------------------------------------------- /website/src/components/layout/Footer/FooterWrapper.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { space } from 'components/foundations/variables'; 3 | 4 | const FooterWrapper = styled('div')` 5 | margin-top: ${space.xxl}px; 6 | `; 7 | 8 | export default FooterWrapper; 9 | -------------------------------------------------------------------------------- /website/src/components/layout/Footer/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Footer } from './Footer'; 2 | export { default as FooterWrapper } from './FooterWrapper'; 3 | -------------------------------------------------------------------------------- /website/src/components/layout/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled, { css } from 'styled-components'; 3 | import { breakpoints, colors, dimensions, layerIndexes } from 'components/foundations/variables'; 4 | 5 | interface HeaderProps { 6 | navigation?: boolean; 7 | absolute?: boolean; 8 | fixed?: boolean; 9 | } 10 | 11 | const isFixed = css` 12 | @media (min-width: ${breakpoints.lg}px) { 13 | left: ${dimensions.widths.sidebar.lg}px; 14 | } 15 | `; 16 | 17 | const Wrapper = styled('header')` 18 | display: flex; 19 | flex-direction: column; 20 | position: ${props => (props.fixed ? 'fixed' : props.absolute ? 'absolute' : 'relative')}; 21 | top: 0; 22 | left: 0; 23 | width: 100%; 24 | height: ${dimensions.heights.header}px; 25 | padding: 0 24px; 26 | background-color: ${props => (props.navigation ? colors.grey01 : colors.white)}; 27 | border-bottom: ${props => (props.navigation ? 'none' : `1px solid ${colors.grey02}`)}; 28 | z-index: ${layerIndexes.stickyNav}; 29 | 30 | ${props => props.fixed && isFixed} 31 | `; 32 | 33 | const Header: React.SFC = ({ children, absolute, fixed, navigation }) => ( 34 | 35 | {children} 36 | 37 | ); 38 | 39 | export default Header; 40 | -------------------------------------------------------------------------------- /website/src/components/layout/Header/HeaderInner.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled, { css } from 'styled-components'; 3 | import { breakpoints } from 'components/foundations/variables'; 4 | 5 | interface HeaderInnerProps { 6 | className?: string; 7 | contents?: 'space-around' | 'space-between' | 'space-evenly' | 'flex-start' | 'flex-end'; 8 | hideOnMobile?: boolean; 9 | hideOnDesktop?: boolean; 10 | } 11 | 12 | const HideOnMobile = css` 13 | @media (max-width: ${breakpoints.lg - 1}px) { 14 | display: none; 15 | } 16 | `; 17 | 18 | const HideOnDesktop = css` 19 | @media (min-width: ${breakpoints.lg}px) { 20 | display: none; 21 | } 22 | `; 23 | 24 | const Wrapper = styled('div')` 25 | display: flex; 26 | flex-direction: row; 27 | align-items: center; 28 | flex: 1; 29 | justify-content: ${props => props.contents}; 30 | 31 | ${props => props.hideOnMobile && HideOnMobile} 32 | ${props => props.hideOnDesktop && HideOnDesktop} 33 | `; 34 | 35 | const HeaderInner: React.SFC = ({ children, className, contents, ...rest }) => ( 36 | 37 | {children} 38 | 39 | ); 40 | 41 | HeaderInner.defaultProps = { 42 | className: undefined, 43 | contents: 'space-between' 44 | }; 45 | 46 | export default HeaderInner; 47 | -------------------------------------------------------------------------------- /website/src/components/layout/Header/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Header } from './Header'; 2 | export { default as HeaderInner } from './HeaderInner'; 3 | -------------------------------------------------------------------------------- /website/src/components/layout/LayoutMain/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as LayoutMain } from './LayoutMain'; 2 | -------------------------------------------------------------------------------- /website/src/components/layout/LayoutRoot/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as LayoutRoot } from './LayoutRoot'; 2 | -------------------------------------------------------------------------------- /website/src/components/layout/Navigation/index.ts: -------------------------------------------------------------------------------- 1 | import Navigation from './Navigation'; 2 | import NavButton from './NavButton'; 3 | 4 | export { Navigation, NavButton }; 5 | -------------------------------------------------------------------------------- /website/src/components/layout/Overlay/Overlay.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import styled, { css } from 'styled-components'; 3 | 4 | import { NavigationContext } from '../Navigation/NavigationContext'; 5 | import { breakpoints, layerIndexes } from 'components/foundations/variables'; 6 | 7 | interface OverlayProps { 8 | visible?: boolean; 9 | } 10 | 11 | const Visible = css` 12 | @media (max-width: ${breakpoints.lg - 1}px) { 13 | opacity: 1; 14 | visibility: visible; 15 | } 16 | `; 17 | 18 | const Root = styled('div')` 19 | position: fixed; 20 | top: 0; 21 | left: 0; 22 | bottom: 0; 23 | right: 0; 24 | background: rgba(0, 31, 63, 0.6); 25 | z-index: ${layerIndexes.overlay}; 26 | opacity: 0; 27 | visibility: hidden; 28 | transition: all 0.3s ease; 29 | 30 | ${props => props.visible && Visible} 31 | `; 32 | 33 | const Overlay: React.FC = () => { 34 | const { state } = React.useContext(NavigationContext); 35 | 36 | return ; 37 | }; 38 | 39 | export default Overlay; 40 | -------------------------------------------------------------------------------- /website/src/components/layout/Overlay/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Overlay } from './Overlay'; 2 | -------------------------------------------------------------------------------- /website/src/components/layout/Page/NotFoundWrapper.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const NotFoundWrapper = styled('div')` 4 | display: flex; 5 | flex: 1 1 auto; 6 | align-items: center; 7 | justify-content: center; 8 | padding: 0; 9 | `; 10 | 11 | export default NotFoundWrapper; 12 | -------------------------------------------------------------------------------- /website/src/components/layout/Page/Page.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { dimensions } from 'components/foundations/variables'; 3 | 4 | interface PageProps { 5 | docsPage?: boolean; 6 | } 7 | 8 | const Page = styled('main')` 9 | display: flex; 10 | flex-direction: column; 11 | flex: 1 1 auto; 12 | position: relative; 13 | margin-top: ${dimensions.heights.header}px; 14 | padding: 0; 15 | `; 16 | 17 | export default Page; 18 | -------------------------------------------------------------------------------- /website/src/components/layout/Page/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Page } from './Page'; 2 | export { default as NotFoundWrapper } from './NotFoundWrapper'; 3 | -------------------------------------------------------------------------------- /website/src/components/page/Markdown/MarkdownContent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { space, breakpoints } from 'components/foundations/variables'; 4 | 5 | interface MarkdownContentProps { 6 | className?: string; 7 | html?: string; 8 | } 9 | 10 | const MarkdownContent: React.SFC = ({ className, html, children }) => { 11 | if (html) { 12 | return
; 13 | } 14 | 15 | return
{children}
; 16 | }; 17 | 18 | export default styled(MarkdownContent)` 19 | .gatsby-highlight { 20 | margin: ${space.sm}px 0; 21 | } 22 | 23 | a[href^='#fn-'], 24 | a[href^='#fnref-'] { 25 | display: inline-block; 26 | margin-left: 0.1rem; 27 | font-weight: bold; 28 | } 29 | 30 | .footnotes { 31 | margin-top: 2rem; 32 | font-size: 85%; 33 | li[id^='fn-'] { 34 | p { 35 | // Remark for some reason puts the footnote reflink *after* the 'p' tag. 36 | display: inline; 37 | } 38 | } 39 | } 40 | 41 | .lead { 42 | font-size: 1.25rem; 43 | font-weight: 300; 44 | 45 | @media (min-width: ${breakpoints.md}) { 46 | font-size: 1.5rem; 47 | } 48 | } 49 | `; 50 | -------------------------------------------------------------------------------- /website/src/components/page/Markdown/index.ts: -------------------------------------------------------------------------------- 1 | export { default as MarkdownContent } from './MarkdownContent'; 2 | -------------------------------------------------------------------------------- /website/src/components/ui/Pagination/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Pagination } from './Pagination'; 2 | -------------------------------------------------------------------------------- /website/src/interfaces/gatsby.ts: -------------------------------------------------------------------------------- 1 | export interface SiteAuthor { 2 | name: string; 3 | url: string; 4 | email: string; 5 | } 6 | 7 | export interface SiteMetadata { 8 | title: string; 9 | sidebarTitle: string; 10 | description: string; 11 | siteUrl: string; 12 | keywords: string; 13 | author: SiteAuthor; 14 | } 15 | -------------------------------------------------------------------------------- /website/src/interfaces/nodes.ts: -------------------------------------------------------------------------------- 1 | export interface HeaderMenuItem { 2 | id: string; 3 | label: string; 4 | href: string; 5 | external: boolean; 6 | } 7 | 8 | export interface TocItem { 9 | id: string; 10 | slug: string; 11 | title: string; 12 | } 13 | 14 | export interface MenuNode { 15 | title: string; 16 | items: TocItem[]; 17 | } 18 | 19 | export interface Edge { 20 | node: T; 21 | } 22 | -------------------------------------------------------------------------------- /website/src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { graphql, Link } from 'gatsby'; 4 | import Helmet from 'react-helmet'; 5 | import { RouteComponentProps } from '@reach/router'; 6 | 7 | import { Page, NotFoundWrapper } from 'components/layout/Page'; 8 | import { SiteMetadata } from 'interfaces/gatsby'; 9 | import { Heading, Text } from 'components/foundations'; 10 | import IndexLayout from 'layouts'; 11 | 12 | interface Props extends RouteComponentProps { 13 | data: { 14 | site: { 15 | siteMetadata: SiteMetadata; 16 | }; 17 | }; 18 | } 19 | 20 | const NotFoundPage: React.SFC = ({ data }) => ( 21 | 22 | 23 | 24 | 404: Page not found. · {data.site.siteMetadata.title} 25 | 26 | 27 | 28 | 29 | 404 30 | 31 | 32 | We can't find the page you're looking for. 33 | 34 | 35 | Go back? 36 | 37 | 38 | 39 | 40 | 41 | ); 42 | 43 | export default NotFoundPage; 44 | 45 | export const query = graphql` 46 | query NotFoundPageQuery { 47 | site { 48 | siteMetadata { 49 | title 50 | description 51 | siteUrl 52 | keywords 53 | author { 54 | name 55 | url 56 | email 57 | } 58 | } 59 | } 60 | } 61 | `; 62 | 63 | const Inner = styled('div')` 64 | text-align: center; 65 | `; 66 | -------------------------------------------------------------------------------- /website/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.bmp' { 2 | const src: string; 3 | export default src; 4 | } 5 | 6 | declare module '*.gif' { 7 | const src: string; 8 | export default src; 9 | } 10 | 11 | declare module '*.jpg' { 12 | const src: string; 13 | export default src; 14 | } 15 | 16 | declare module '*.jpeg' { 17 | const src: string; 18 | export default src; 19 | } 20 | 21 | declare module '*.png' { 22 | const src: string; 23 | export default src; 24 | } 25 | 26 | declare module '*.webp' { 27 | const src: string; 28 | export default src; 29 | } 30 | 31 | // type shims for CSS modules 32 | 33 | declare module '*.module.css' { 34 | const classes: { [key: string]: string }; 35 | export default classes; 36 | } 37 | 38 | declare module '*.module.scss' { 39 | const classes: { [key: string]: string }; 40 | export default classes; 41 | } 42 | 43 | declare module '*.module.sass' { 44 | const classes: { [key: string]: string }; 45 | export default classes; 46 | } 47 | 48 | declare module '*.json' { 49 | const name: any; 50 | export = name; 51 | } 52 | 53 | declare module 'rehype-react'; 54 | 55 | declare module '@reach/skip-nav' { 56 | class SkipNavLink extends React.Component {} 57 | class SkipNavContent extends React.Component {} 58 | } 59 | -------------------------------------------------------------------------------- /website/src/utils/helpers.ts: -------------------------------------------------------------------------------- 1 | import { LinkGetProps } from '@reach/router'; 2 | import { MenuNode, TocItem, Edge } from 'interfaces/nodes'; 3 | import { Color, themeProps, Space } from 'components/foundations'; 4 | 5 | export const getColor = (colorKey: Color) => themeProps.colors[colorKey]; 6 | 7 | export const getSpacing = (spaceKey: Space) => themeProps.space[spaceKey]; 8 | 9 | export const getPageById = (sectionList: Edge[], templateFile?: string) => { 10 | if (!templateFile) { 11 | return undefined; 12 | } 13 | 14 | const sectionItems = sectionList.map(({ node }) => node.items); 15 | const flattenedSectionItems: TocItem[] = ([] as TocItem[]).concat(...sectionItems); 16 | 17 | return flattenedSectionItems.find(item => item.id === templateFile); 18 | }; 19 | 20 | /** Workaround for activeClassName: https://github.com/gatsbyjs/gatsby/issues/7737 */ 21 | export const isActive = ({ isPartiallyCurrent }: LinkGetProps) => { 22 | return isPartiallyCurrent ? { className: 'active' } : {}; 23 | }; 24 | -------------------------------------------------------------------------------- /website/src/utils/renderAst.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import rehypeReact from 'rehype-react'; 3 | import { h1, h2, h3, h4, h5, h6, p, ul, li, table } from 'components/page/Markdown/MarkdownComponents'; 4 | 5 | export interface ComponentMap { 6 | [key: string]: React.ComponentType; 7 | } 8 | 9 | export function renderAst(markdownAst: any, additionalComponents: ComponentMap = {}) { 10 | const rehype = new rehypeReact({ 11 | createElement: React.createElement, 12 | components: { 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | p, 20 | ul, 21 | li, 22 | table, 23 | ...additionalComponents 24 | } 25 | }); 26 | return rehype.Compiler(markdownAst); 27 | } 28 | 29 | export default renderAst; 30 | -------------------------------------------------------------------------------- /website/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Omits a property from an object. 3 | * 4 | * @borrows https://github.com/dfee/rbx/blob/HEAD/src/types.ts#L3 5 | */ 6 | export type Omit = Pick>; 7 | 8 | /** 9 | * Takes two items and merges its properties. If there’s a collision, it prefers the *first* item. 10 | * 11 | * @borrows https://github.com/dfee/rbx/blob/HEAD/src/types.ts#L3 12 | */ 13 | export type Prefer = P & Omit; 14 | -------------------------------------------------------------------------------- /website/static/img/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs/node-addon-examples/4213d4c9d07996ae68629c67926251e117f8e52a/website/static/img/homepage.png -------------------------------------------------------------------------------- /website/static/img/new-repo-from-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs/node-addon-examples/4213d4c9d07996ae68629c67926251e117f8e52a/website/static/img/new-repo-from-template.png -------------------------------------------------------------------------------- /website/static/img/use-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs/node-addon-examples/4213d4c9d07996ae68629c67926251e117f8e52a/website/static/img/use-template.png --------------------------------------------------------------------------------