This is an automated email from the ASF dual-hosted git repository. voidmatcha pushed a commit to branch ZEPPELIN-6670-shared-core-contract in repository https://gitbox.apache.org/repos/asf/zeppelin.git
commit a7d77d3aa8293ffc365b45becf24cdfb1d32f8f5 Author: YONGJAE LEE <[email protected]> AuthorDate: Mon Aug 31 21:03:32 2026 +0900 [ZEPPELIN-6670] Add Shared Notebook Core mount contract --- zeppelin-web-angular/angular.json | 21 +++ zeppelin-web-angular/eslint.config.js | 11 +- zeppelin-web-angular/package.json | 5 +- zeppelin-web-angular/pom.xml | 24 +++ .../zeppelin-notebook-core/ng-package.json | 7 + .../projects/zeppelin-notebook-core/package.json | 8 + .../src/host-remote-contract.spec.ts | 62 +++++++ .../src/host-remote-contract.ts} | 20 +- .../src/import-boundary.spec.ts | 206 +++++++++++++++++++++ .../src/public-api.ts} | 4 +- .../projects/zeppelin-notebook-core/tsconfig.json | 11 ++ .../zeppelin-notebook-core/tsconfig.spec.json | 9 + .../projects/zeppelin-react/package-lock.json | 12 ++ .../projects/zeppelin-react/package.json | 1 + .../projects/zeppelin-react/src/main.ts | 1 + .../src/{main.ts => notebookCoreContract.ts} | 4 +- .../projects/zeppelin-react/tsconfig.json | 2 + .../tsconfig.notebook-core.dist.json | 8 + .../zeppelin-react/tsconfig.notebook-core.json | 5 + .../projects/zeppelin-react/webpack.config.js | 1 + zeppelin-web-angular/src/tsconfig.spec.json | 2 + zeppelin-web-angular/tsconfig.base.json | 2 + .../main.ts => vitest.notebook-core.config.mts} | 11 +- 23 files changed, 421 insertions(+), 16 deletions(-) diff --git a/zeppelin-web-angular/angular.json b/zeppelin-web-angular/angular.json index fa7d20ec48..0280764be6 100644 --- a/zeppelin-web-angular/angular.json +++ b/zeppelin-web-angular/angular.json @@ -177,6 +177,27 @@ } } }, + "zeppelin-notebook-core": { + "projectType": "library", + "root": "projects/zeppelin-notebook-core", + "sourceRoot": "projects/zeppelin-notebook-core/src", + "prefix": "lib", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:ng-packagr", + "options": { + "tsConfig": "projects/zeppelin-notebook-core/tsconfig.json", + "project": "projects/zeppelin-notebook-core/ng-package.json" + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": ["projects/zeppelin-notebook-core/**/*.ts"] + } + } + } + }, "zeppelin-sdk": { "projectType": "library", "root": "projects/zeppelin-sdk", diff --git a/zeppelin-web-angular/eslint.config.js b/zeppelin-web-angular/eslint.config.js index 23b87ff73a..917aef9c69 100644 --- a/zeppelin-web-angular/eslint.config.js +++ b/zeppelin-web-angular/eslint.config.js @@ -152,7 +152,11 @@ module.exports = tseslint.config( }, { // Library projects publish under the `lib` selector prefix, not `zeppelin`. - files: ['projects/zeppelin-sdk/**/*.ts', 'projects/zeppelin-visualization/**/*.ts'], + files: [ + 'projects/zeppelin-notebook-core/**/*.ts', + 'projects/zeppelin-sdk/**/*.ts', + 'projects/zeppelin-visualization/**/*.ts' + ], rules: { '@angular-eslint/component-selector': ['error', { type: 'element', prefix: 'lib', style: 'kebab-case' }], '@angular-eslint/directive-selector': ['error', { type: 'attribute', prefix: 'lib', style: 'camelCase' }] @@ -163,8 +167,9 @@ module.exports = tseslint.config( // *.spec.ts. Point type-aware linting at the spec program explicitly. files: [ 'src/**/*.spec.ts', - 'projects/zeppelin-{sdk,visualization}/**/*.spec.ts', + 'projects/zeppelin-{notebook-core,sdk,visualization}/**/*.spec.ts', 'test/test-setup.ts', + 'vitest.notebook-core.config.mts', 'vitest.shell.config.mts' ], languageOptions: { @@ -176,7 +181,7 @@ module.exports = tseslint.config( }, { // Catch specs that cannot fail, as eslint-plugin-playwright does for e2e. - files: ['src/**/*.spec.ts', 'projects/zeppelin-{sdk,visualization}/**/*.spec.ts'], + files: ['src/**/*.spec.ts', 'projects/zeppelin-{notebook-core,sdk,visualization}/**/*.spec.ts'], plugins: { vitest }, rules: { 'vitest/expect-expect': 'error', diff --git a/zeppelin-web-angular/package.json b/zeppelin-web-angular/package.json index 8acdda44a6..10b0123122 100644 --- a/zeppelin-web-angular/package.json +++ b/zeppelin-web-angular/package.json @@ -11,7 +11,8 @@ "build": "npm run build:projects && npm run build:react && npm run build:angular", "build:angular": "ng build --configuration production", "build:react": "cd projects/zeppelin-react && npm run build", - "build:projects": "npm run build-project:sdk && npm run build-project:vis", + "build:projects": "npm run build-project:sdk && npm run build-project:notebook-core && npm run build-project:vis", + "build-project:notebook-core": "ng build --project zeppelin-notebook-core", "build-project:sdk": "ng build --project zeppelin-sdk", "check:websocket-contract": "node --test scripts/check-websocket-contract.test.js && node scripts/check-websocket-contract.js", "build-project:vis": "ng build --project zeppelin-visualization", @@ -19,6 +20,8 @@ "lint:fix": "cross-env NODE_OPTIONS='--max-old-space-size=8192' ng lint --fix && npm run lint:fix:react && prettier --write \"**/*.{ts,tsx,mts,js,json,css,html}\"", "lint:react": "cd projects/zeppelin-react && npm run lint", "lint:fix:react": "cd projects/zeppelin-react && npm run lint:fix", + "typecheck:notebook-core": "tsc -p projects/zeppelin-notebook-core/tsconfig.json --noEmit && tsc -p projects/zeppelin-notebook-core/tsconfig.spec.json --noEmit && npm run build-project:notebook-core && tsc -p projects/zeppelin-react/tsconfig.notebook-core.dist.json --noEmit && tsc -p projects/zeppelin-react/tsconfig.notebook-core.json --noEmit", + "test:notebook-core": "vitest run --config vitest.notebook-core.config.mts", "test:shell": "vitest run --config vitest.shell.config.mts", "test:eslint-rules": "node --test eslint-rules/", "e2e": "playwright test", diff --git a/zeppelin-web-angular/pom.xml b/zeppelin-web-angular/pom.xml index 3f2fee17ff..6d0123fc7a 100644 --- a/zeppelin-web-angular/pom.xml +++ b/zeppelin-web-angular/pom.xml @@ -129,6 +129,30 @@ </configuration> </execution> + <execution> + <id>npm typecheck notebook core</id> + <goals> + <goal>npm</goal> + </goals> + <phase>test</phase> + <configuration> + <skip>${skipTests}</skip> + <arguments>run typecheck:notebook-core</arguments> + </configuration> + </execution> + + <execution> + <id>npm test notebook core</id> + <goals> + <goal>npm</goal> + </goals> + <phase>test</phase> + <configuration> + <skip>${skipTests}</skip> + <arguments>run test:notebook-core</arguments> + </configuration> + </execution> + <execution> <id>npm e2e</id> <goals> diff --git a/zeppelin-web-angular/projects/zeppelin-notebook-core/ng-package.json b/zeppelin-web-angular/projects/zeppelin-notebook-core/ng-package.json new file mode 100644 index 0000000000..b7ef11c43a --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/zeppelin-notebook-core", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/zeppelin-web-angular/projects/zeppelin-notebook-core/package.json b/zeppelin-web-angular/projects/zeppelin-notebook-core/package.json new file mode 100644 index 0000000000..5431edbe97 --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/package.json @@ -0,0 +1,8 @@ +{ + "name": "@zeppelin/notebook-core", + "version": "0.0.1", + "sideEffects": false, + "dependencies": { + "tslib": "^2.0.0" + } +} diff --git a/zeppelin-web-angular/projects/zeppelin-notebook-core/src/host-remote-contract.spec.ts b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/host-remote-contract.spec.ts new file mode 100644 index 0000000000..6e1c072877 --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/host-remote-contract.spec.ts @@ -0,0 +1,62 @@ +// @vitest-environment node + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, expect, it } from 'vitest'; + +import type { + NotebookCorePort, + NotebookCoreRemoteProps, + NotebookCoreSnapshot, + NotebookCoreSnapshotListener +} from './public-api'; + +const fakeCorePort = (initialSnapshot: NotebookCoreSnapshot) => { + let snapshot = initialSnapshot; + const listeners = new Set<NotebookCoreSnapshotListener>(); + const core: NotebookCorePort = { + getSnapshot: () => snapshot, + subscribe: listener => { + listeners.add(listener); + return () => listeners.delete(listener); + } + }; + + return { + core, + publish: (nextSnapshot: NotebookCoreSnapshot) => { + snapshot = nextSnapshot; + for (const listener of listeners) { + listener(); + } + } + }; +}; + +describe('notebook core host and remote contract', () => { + it('lets host and remote share one read-only snapshot source through getSnapshot and subscribe', () => { + const host = fakeCorePort({ noteId: '2A94M5J1Z', revisionId: null }); + const remoteProps: NotebookCoreRemoteProps = { core: host.core }; + const snapshots: unknown[] = []; + + expect(remoteProps.core).toBe(host.core); + + const unsubscribe = remoteProps.core.subscribe(() => snapshots.push(remoteProps.core.getSnapshot())); + host.publish({ noteId: '2A94M5J1Z', revisionId: 'rev-1' }); + unsubscribe(); + host.publish({ noteId: '2A94M5J1Z', revisionId: 'rev-2' }); + + expect(snapshots).toEqual([{ noteId: '2A94M5J1Z', revisionId: 'rev-1' }]); + expect(remoteProps.core.getSnapshot()).toEqual({ noteId: '2A94M5J1Z', revisionId: 'rev-2' }); + }); +}); diff --git a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/host-remote-contract.ts similarity index 54% copy from zeppelin-web-angular/projects/zeppelin-react/src/main.ts copy to zeppelin-web-angular/projects/zeppelin-notebook-core/src/host-remote-contract.ts index ce7edc883f..9a62c63e67 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/host-remote-contract.ts @@ -10,6 +10,20 @@ * limitations under the License. */ -export { ConfigurationTable, mount as mountConfigurationTable } from './pages/ConfigurationTable'; -export { PublishedParagraph, mount } from './pages/PublishedParagraph'; -export { ParagraphFooter, mount as mountParagraphFooter } from './components/paragraph/ParagraphFooter'; +export type NotebookCoreSnapshot = Readonly<{ + noteId: string; + revisionId: string | null; +}>; + +export type NotebookCoreUnsubscribe = () => void; + +export type NotebookCoreSnapshotListener = () => void; + +export type NotebookCorePort = Readonly<{ + getSnapshot: () => NotebookCoreSnapshot; + subscribe: (listener: NotebookCoreSnapshotListener) => NotebookCoreUnsubscribe; +}>; + +export type NotebookCoreRemoteProps = Readonly<{ + core: NotebookCorePort; +}>; diff --git a/zeppelin-web-angular/projects/zeppelin-notebook-core/src/import-boundary.spec.ts b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/import-boundary.spec.ts new file mode 100644 index 0000000000..800ac1c9a4 --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/import-boundary.spec.ts @@ -0,0 +1,206 @@ +// @vitest-environment node + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { readdirSync, readFileSync, statSync } from 'node:fs'; +import { join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import ts from 'typescript'; +import { describe, expect, it } from 'vitest'; + +const sourceRoot = fileURLToPath(new URL('.', import.meta.url)); +const zeppelinWebAngularRoot = resolve(sourceRoot, '../../..'); +const reactNotebookCoreBoundaryFiles = [ + resolve(zeppelinWebAngularRoot, 'projects/zeppelin-react/src/main.ts'), + resolve(zeppelinWebAngularRoot, 'projects/zeppelin-react/src/notebookCoreContract.ts') +]; +const forbiddenModulePrefixes = [ + '@angular/', + '@zeppelin/sdk', + 'react', + 'react-dom', + 'react-redux', + 'react-router', + 'react-router-dom', + 'rxjs', + 'axios' +]; +const forbiddenReactNotebookCoreConsumerModulePrefixes = [ + '@angular/common/http', + '@zeppelin/sdk', + 'axios', + 'rxjs/webSocket' +]; +const forbiddenGlobals = new Set(['fetch', 'WebSocket', 'XMLHttpRequest']); +const transportGlobalOwners = new Set(['globalThis', 'window']); + +const sourceFiles = (dir: string): string[] => + readdirSync(dir).flatMap(entry => { + const path = join(dir, entry); + if (statSync(path).isDirectory()) { + return sourceFiles(path); + } + return isCheckedSourceFile(path) ? [path] : []; + }); + +const checkedSourceExtensions = ['.ts', '.tsx', '.mts', '.cts']; + +const isCheckedSourceFile = (path: string): boolean => { + return checkedSourceExtensions.some(extension => path.endsWith(extension)) && !/\.spec\.[cm]?tsx?$/.test(path); +}; + +describe('notebook core import boundary', () => { + it('stays framework-neutral and transport-neutral', () => { + const violations = sourceFiles(sourceRoot).flatMap(path => { + const source = readFileSync(path, 'utf8'); + return findViolations(path, source); + }); + + expect(violations).toEqual([]); + }); + + it('keeps the React notebook core adapter independent from Zeppelin transport implementations', () => { + const violations = reactNotebookCoreBoundaryFiles.flatMap(path => { + const source = readFileSync(path, 'utf8'); + return findViolations(path, source, forbiddenReactNotebookCoreConsumerModulePrefixes); + }); + + expect(formatViolations(violations)).toEqual([]); + }); + + it('ignores forbidden words in comments and string values', () => { + const source = `// React may render this later.\nexport const note = 'fetch over WebSocket';`; + + expect(findViolations('comment-fixture.ts', source)).toEqual([]); + }); + + it('rejects static, dynamic and direct transport dependencies', () => { + const source = [ + `import type { OP } from '@zeppelin/sdk';`, + `export { useMemo } from 'react';`, + `type LeakedMessage = import('@zeppelin/sdk').Message;`, + `import { createRoot } from 'react-dom/client';`, + `import { Provider } from 'react-redux';`, + `const router = () => import('react-router-dom');`, + `const load = () => import('rxjs/operators');`, + `const request = () => fetch('/api/notebook');`, + `const socket = new globalThis.WebSocket('/ws');`, + `const xhr = new window.XMLHttpRequest();` + ].join('\n'); + + expect(findViolations('violation-fixture.ts', source)).toEqual([ + 'violation-fixture.ts: import @zeppelin/sdk', + 'violation-fixture.ts: import react', + 'violation-fixture.ts: import @zeppelin/sdk', + 'violation-fixture.ts: import react-dom/client', + 'violation-fixture.ts: import react-redux', + 'violation-fixture.ts: import react-router-dom', + 'violation-fixture.ts: import rxjs/operators', + 'violation-fixture.ts: global fetch', + 'violation-fixture.ts: global WebSocket', + 'violation-fixture.ts: global XMLHttpRequest' + ]); + }); +}); + +const findViolations = ( + path: string, + source: string, + forbiddenPrefixes: readonly string[] = forbiddenModulePrefixes +): string[] => { + const sourceFile = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true, getScriptKind(path)); + const violations: string[] = []; + + const visit = (node: ts.Node): void => { + const moduleSpecifier = getModuleSpecifier(node); + if (moduleSpecifier && forbiddenPrefixes.some(prefix => matchesModulePrefix(moduleSpecifier, prefix))) { + violations.push(`${path}: import ${moduleSpecifier}`); + } + + const forbiddenGlobal = getForbiddenTransportGlobalName(node); + if (forbiddenGlobal) { + violations.push(`${path}: global ${forbiddenGlobal}`); + } + + ts.forEachChild(node, visit); + }; + + visit(sourceFile); + return violations; +}; + +const formatViolations = (violations: readonly string[]): string[] => { + return violations.map(violation => violation.replace(`${zeppelinWebAngularRoot}/`, '')); +}; + +const getScriptKind = (path: string): ts.ScriptKind => { + if (path.endsWith('.tsx')) { + return ts.ScriptKind.TSX; + } + return ts.ScriptKind.TS; +}; + +const getModuleSpecifier = (node: ts.Node): string | null => { + if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier) { + return ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : null; + } + if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal)) { + return node.argument.literal.text; + } + if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference)) { + const expression = node.moduleReference.expression; + return expression && ts.isStringLiteral(expression) ? expression.text : null; + } + if ( + ts.isCallExpression(node) && + node.arguments.length === 1 && + ts.isStringLiteral(node.arguments[0]) && + (node.expression.kind === ts.SyntaxKind.ImportKeyword || + (ts.isIdentifier(node.expression) && node.expression.text === 'require')) + ) { + return node.arguments[0].text; + } + return null; +}; + +const matchesModulePrefix = (moduleSpecifier: string, prefix: string): boolean => { + return moduleSpecifier === prefix || moduleSpecifier.startsWith(prefix.endsWith('/') ? prefix : `${prefix}/`); +}; + +const isRuntimeIdentifier = (node: ts.Identifier): boolean => { + const parent = node.parent; + return !( + (ts.isPropertyAccessExpression(parent) && parent.name === node) || + (ts.isPropertyAssignment(parent) && parent.name === node) || + (ts.isMethodDeclaration(parent) && parent.name === node) || + (ts.isPropertyDeclaration(parent) && parent.name === node) || + (ts.isTypeReferenceNode(parent) && parent.typeName === node) + ); +}; + +const getForbiddenTransportGlobalName = (node: ts.Node): string | null => { + if (ts.isIdentifier(node) && forbiddenGlobals.has(node.text) && isRuntimeIdentifier(node)) { + return node.text; + } + if ( + ts.isPropertyAccessExpression(node) && + ts.isIdentifier(node.name) && + forbiddenGlobals.has(node.name.text) && + ts.isIdentifier(node.expression) && + transportGlobalOwners.has(node.expression.text) + ) { + return node.name.text; + } + return null; +}; diff --git a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/public-api.ts similarity index 66% copy from zeppelin-web-angular/projects/zeppelin-react/src/main.ts copy to zeppelin-web-angular/projects/zeppelin-notebook-core/src/public-api.ts index ce7edc883f..0d82f21dde 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/src/public-api.ts @@ -10,6 +10,4 @@ * limitations under the License. */ -export { ConfigurationTable, mount as mountConfigurationTable } from './pages/ConfigurationTable'; -export { PublishedParagraph, mount } from './pages/PublishedParagraph'; -export { ParagraphFooter, mount as mountParagraphFooter } from './components/paragraph/ParagraphFooter'; +export * from './host-remote-contract'; diff --git a/zeppelin-web-angular/projects/zeppelin-notebook-core/tsconfig.json b/zeppelin-web-angular/projects/zeppelin-notebook-core/tsconfig.json new file mode 100644 index 0000000000..6992356b4d --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2020", + "declaration": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["**/*.spec.ts"] +} diff --git a/zeppelin-web-angular/projects/zeppelin-notebook-core/tsconfig.spec.json b/zeppelin-web-angular/projects/zeppelin-notebook-core/tsconfig.spec.json new file mode 100644 index 0000000000..397347a76a --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-notebook-core/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec/notebook-core", + "types": ["node"] + }, + "include": ["src/**/*.spec.ts"], + "exclude": [] +} diff --git a/zeppelin-web-angular/projects/zeppelin-react/package-lock.json b/zeppelin-web-angular/projects/zeppelin-react/package-lock.json index c988c10777..d1f92ce00c 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/package-lock.json +++ b/zeppelin-web-angular/projects/zeppelin-react/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "dependencies": { "@ant-design/icons": "5.4.0", + "@zeppelin/notebook-core": "file:../zeppelin-notebook-core", "@zeppelin/sdk": "file:../zeppelin-sdk", "ansi-to-react": "6.2.6", "antd": "5.21.0", @@ -52,6 +53,13 @@ "webpack-dev-server": "6.0.0" } }, + "../zeppelin-notebook-core": { + "name": "@zeppelin/notebook-core", + "version": "0.0.1", + "dependencies": { + "tslib": "^2.0.0" + } + }, "../zeppelin-sdk": { "name": "@zeppelin/sdk", "version": "0.0.1", @@ -2733,6 +2741,10 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@zeppelin/notebook-core": { + "resolved": "../zeppelin-notebook-core", + "link": true + }, "node_modules/@zeppelin/sdk": { "resolved": "../zeppelin-sdk", "link": true diff --git a/zeppelin-web-angular/projects/zeppelin-react/package.json b/zeppelin-web-angular/projects/zeppelin-react/package.json index c6441af70b..3eb2781ed0 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/package.json +++ b/zeppelin-web-angular/projects/zeppelin-react/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@ant-design/icons": "5.4.0", + "@zeppelin/notebook-core": "file:../zeppelin-notebook-core", "@zeppelin/sdk": "file:../zeppelin-sdk", "ansi-to-react": "6.2.6", "highlight.js": "^9.15.8", diff --git a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts b/zeppelin-web-angular/projects/zeppelin-react/src/main.ts index ce7edc883f..66f7f95a0a 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts +++ b/zeppelin-web-angular/projects/zeppelin-react/src/main.ts @@ -13,3 +13,4 @@ export { ConfigurationTable, mount as mountConfigurationTable } from './pages/ConfigurationTable'; export { PublishedParagraph, mount } from './pages/PublishedParagraph'; export { ParagraphFooter, mount as mountParagraphFooter } from './components/paragraph/ParagraphFooter'; +export type { NotebookCoreRemoteProps } from './notebookCoreContract'; diff --git a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts b/zeppelin-web-angular/projects/zeppelin-react/src/notebookCoreContract.ts similarity index 66% copy from zeppelin-web-angular/projects/zeppelin-react/src/main.ts copy to zeppelin-web-angular/projects/zeppelin-react/src/notebookCoreContract.ts index ce7edc883f..1eceaf420c 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts +++ b/zeppelin-web-angular/projects/zeppelin-react/src/notebookCoreContract.ts @@ -10,6 +10,4 @@ * limitations under the License. */ -export { ConfigurationTable, mount as mountConfigurationTable } from './pages/ConfigurationTable'; -export { PublishedParagraph, mount } from './pages/PublishedParagraph'; -export { ParagraphFooter, mount as mountParagraphFooter } from './components/paragraph/ParagraphFooter'; +export type { NotebookCoreRemoteProps } from '@zeppelin/notebook-core'; diff --git a/zeppelin-web-angular/projects/zeppelin-react/tsconfig.json b/zeppelin-web-angular/projects/zeppelin-react/tsconfig.json index 3dba52c585..5c4f37847b 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/tsconfig.json +++ b/zeppelin-web-angular/projects/zeppelin-react/tsconfig.json @@ -19,6 +19,8 @@ "baseUrl": "src", "paths": { "@/*": ["./*"], + "@zeppelin/notebook-core": ["../../zeppelin-notebook-core/src/public-api.ts"], + "@zeppelin/notebook-core/*": ["../../zeppelin-notebook-core/src/*"], "@zeppelin/sdk": ["../../zeppelin-sdk/src"], "@zeppelin/sdk/*": ["../../zeppelin-sdk/src/*"] } diff --git a/zeppelin-web-angular/projects/zeppelin-react/tsconfig.notebook-core.dist.json b/zeppelin-web-angular/projects/zeppelin-react/tsconfig.notebook-core.dist.json new file mode 100644 index 0000000000..12ee26106c --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-react/tsconfig.notebook-core.dist.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.notebook-core.json", + "compilerOptions": { + "paths": { + "@zeppelin/notebook-core": ["../../../dist/zeppelin-notebook-core"] + } + } +} diff --git a/zeppelin-web-angular/projects/zeppelin-react/tsconfig.notebook-core.json b/zeppelin-web-angular/projects/zeppelin-react/tsconfig.notebook-core.json new file mode 100644 index 0000000000..8516d2cf5f --- /dev/null +++ b/zeppelin-web-angular/projects/zeppelin-react/tsconfig.notebook-core.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/notebookCoreContract.ts"], + "exclude": [] +} diff --git a/zeppelin-web-angular/projects/zeppelin-react/webpack.config.js b/zeppelin-web-angular/projects/zeppelin-react/webpack.config.js index d7de57b3d9..fc58f1af76 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/webpack.config.js +++ b/zeppelin-web-angular/projects/zeppelin-react/webpack.config.js @@ -39,6 +39,7 @@ module.exports = (_env, argv) => { modules: ['node_modules', path.resolve(__dirname, '../../node_modules')], alias: { '@': path.resolve(__dirname, 'src'), + '@zeppelin/notebook-core': path.resolve(__dirname, '../zeppelin-notebook-core/src/public-api.ts'), '@zeppelin/sdk': path.resolve(__dirname, '../zeppelin-sdk/src') } }, diff --git a/zeppelin-web-angular/src/tsconfig.spec.json b/zeppelin-web-angular/src/tsconfig.spec.json index e4233dd1d4..1a42d73b0c 100644 --- a/zeppelin-web-angular/src/tsconfig.spec.json +++ b/zeppelin-web-angular/src/tsconfig.spec.json @@ -6,9 +6,11 @@ }, "include": [ "**/*.spec.ts", + "../projects/zeppelin-notebook-core/**/*.spec.ts", "../projects/zeppelin-sdk/**/*.spec.ts", "../projects/zeppelin-visualization/**/*.spec.ts", "../test/test-setup.ts", + "../vitest.notebook-core.config.mts", "../vitest.shell.config.mts" ], "exclude": [] diff --git a/zeppelin-web-angular/tsconfig.base.json b/zeppelin-web-angular/tsconfig.base.json index 7fee7c8e79..ee189af234 100644 --- a/zeppelin-web-angular/tsconfig.base.json +++ b/zeppelin-web-angular/tsconfig.base.json @@ -6,6 +6,8 @@ "@zeppelin/*": ["./src/app/*", "./src/environments/*"], "@zeppelin/visualization": ["dist/zeppelin-visualization"], "@zeppelin/visualization/*": ["dist/zeppelin-visualization/*"], + "@zeppelin/notebook-core": ["dist/zeppelin-notebook-core"], + "@zeppelin/notebook-core/*": ["dist/zeppelin-notebook-core/*"], "@zeppelin/sdk": ["dist/zeppelin-sdk"], "@zeppelin/sdk/*": ["dist/zeppelin-sdk/*"] }, diff --git a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts b/zeppelin-web-angular/vitest.notebook-core.config.mts similarity index 66% copy from zeppelin-web-angular/projects/zeppelin-react/src/main.ts copy to zeppelin-web-angular/vitest.notebook-core.config.mts index ce7edc883f..6737451fa2 100644 --- a/zeppelin-web-angular/projects/zeppelin-react/src/main.ts +++ b/zeppelin-web-angular/vitest.notebook-core.config.mts @@ -10,6 +10,11 @@ * limitations under the License. */ -export { ConfigurationTable, mount as mountConfigurationTable } from './pages/ConfigurationTable'; -export { PublishedParagraph, mount } from './pages/PublishedParagraph'; -export { ParagraphFooter, mount as mountParagraphFooter } from './components/paragraph/ParagraphFooter'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + include: ['projects/zeppelin-notebook-core/**/*.spec.ts'] + } +});
