Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions examples/deno-desktop-mastodon/compile-decorators.ts

This file was deleted.

12 changes: 10 additions & 2 deletions examples/deno-desktop-mastodon/mastodon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@ try {

import './app/styles.css';

// Import route modules for side-effect: customElements.define + exports.
// Import route modules to bind their loader/tagName exports for the route
// table below. The @element page classes are compiled by the adapter's
// open:compiled-element transform; v0.44 compiled modules never self-register
// — in adapter-generated SSR/client entries the entry owns every
// customElements.define, so this custom SPA bootstrap must not rely on module
// side effects for page-element registration.
import TimelinePage, { loader as timelineLoader, tagName as timelineTag } from './routes/index.tsx';
import ProfilePage, { loader as profileLoader, tagName as profileTag } from './routes/profile.tsx';
import StatusPage, { loader as statusLoader, tagName as statusTag } from './routes/status.tsx';
import SettingsPage, { tagName as settingsTag } from './routes/settings.tsx';

// Import islands for side-effect: customElements.define registers custom elements.
// Import islands for side effect: definePreactIsland() registers each
// island's custom element at module evaluation. This app ships its own
// index.html client entry, so no adapter-generated entry imports the islands
// — the bootstrap import is what runs that registration.
import './islands/settings-island.tsx';

void TimelinePage;
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-mastodon/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement } from '@openelement/element';
import type { MastodonStatus } from '../app/types.ts';
import { getTimeline } from '../app/api-client.ts';
import StatusCard, { StatusCardSkeleton } from '../components/StatusCard.tsx';
import { element } from '../compile-decorators.ts';

export interface TimelineData {
statuses: MastodonStatus[];
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-mastodon/routes/profile.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement } from '@openelement/element';
import type { MastodonAccount, MastodonStatus } from '../app/types.ts';
import { getProfile, getProfileStatuses } from '../app/api-client.ts';
import { formatCount, stripHtml } from '../app/format.ts';
import StatusCard from '../components/StatusCard.tsx';
import { element } from '../compile-decorators.ts';

export interface ProfileData {
account?: MastodonAccount;
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-mastodon/routes/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element } from '../compile-decorators.ts';
import { element, OpenElement } from '@openelement/element';

export const tagName = 'mastodon-settings';

Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-mastodon/routes/status.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement } from '@openelement/element';
import type { MastodonStatus } from '../app/types.ts';
import { getStatus, getStatusContext } from '../app/api-client.ts';
import StatusCard from '../components/StatusCard.tsx';
import { element } from '../compile-decorators.ts';

export interface StatusData {
status?: MastodonStatus;
Expand Down
15 changes: 0 additions & 15 deletions examples/deno-desktop-reader/compile-decorators.ts

This file was deleted.

15 changes: 11 additions & 4 deletions examples/deno-desktop-reader/reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,13 @@ try {
// CSSStyleSheet constructor may not be available in older browsers
}

// Import route modules for side-effect: customElements.define + exports
// Import route modules to bind their loader/action/tagName exports for the
// route table below. The @element page classes are compiled by the adapter's
// open:compiled-element transform; v0.44 compiled modules never self-register
// — in adapter-generated SSR/client entries the entry owns every
// customElements.define (packages/adapter-vite entry-orchestrator). This
// custom SPA bootstrap therefore must not rely on module side effects for
// page-element registration.
import BookshelfPage, {
action as bookshelfAction,
loader as bookshelfLoader,
Expand All @@ -954,9 +960,10 @@ import SettingsPage, {
} from './routes/settings.tsx';
import WcInteropPage, { tagName as wcInteropTag } from './routes/wc-interop.tsx';

// Import islands for side-effect: definePreactIsland registers custom elements.
// In SPA mode the adapter does not inject an islands bundle, so islands must
// be imported by the bootstrap to ensure their customElements.define() runs.
// Import islands for side effect: definePreactIsland() registers each
// island's custom element at module evaluation. This app ships its own
// index.html client entry, so no adapter-generated entry imports the islands
// — the bootstrap import is what runs that registration.
import './islands/pdf-reader-island.tsx';
import './islands/search-box-island.tsx';
import './islands/sync-status-island.tsx';
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-reader/routes/books/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement, property } from '@openelement/element';
import type { LibraryBook, ReaderNote, ReaderProgress } from '../../app/types.ts';
import { getBookDetails, listBooks, saveNote } from '../../app/api.ts';
import { navigate } from '../../router.ts';
import { element, property } from '../../compile-decorators.ts';

export interface ReadingData {
book: LibraryBook | null;
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-reader/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement, property } from '@openelement/element';
import type { ReaderProgress, ReaderSource } from '../app/types.ts';
import { getBookDetails, listBooks, listSources, syncSource } from '../app/api.ts';
import { navigate } from '../router.ts';
import BookCard from '../components/BookCard.tsx';
import type { LibraryBook } from '../app/types.ts';
import { element, property } from '../compile-decorators.ts';

export interface BookshelfData {
books: LibraryBook[];
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-reader/routes/notes.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement } from '@openelement/element';
import type { LibraryBook, ReaderNote } from '../app/types.ts';
import { deleteNote, listBooks, listNotes } from '../app/api.ts';
import { navigate } from '../router.ts';
import { element } from '../compile-decorators.ts';

export interface NotesData {
allNotes: ReaderNote[];
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-reader/routes/search.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement } from '@openelement/element';
import type { ReaderSearchResult } from '../app/types.ts';
import { searchLibrary } from '../app/api.ts';
import { navigate } from '../router.ts';
import { element } from '../compile-decorators.ts';

export interface SearchData {
query: string;
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-reader/routes/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement, property } from '@openelement/element';
import { addSource, listSources, syncSource } from '../app/api.ts';
import { loadSettings, saveSettings } from '../app/storage.ts';
import { pdfMaxWidth } from '../app/pdf-measure.ts';
import type { ReaderSettings, ReaderSource } from '../app/types.ts';
import { element, property } from '../compile-decorators.ts';

function applyTheme(theme: string): void {
if (theme === 'dark' || theme === 'sepia') {
Expand Down
3 changes: 1 addition & 2 deletions examples/deno-desktop-reader/routes/wc-interop.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/** @jsxImportSource @openelement/element */
import { OpenElement } from '@openelement/element';
import { element, OpenElement, property } from '@openelement/element';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import { element, property } from '../compile-decorators.ts';

// Validates @openelement/ui CE coexistence: open-button / open-card / open-input
// + Preact island all render in shadow DOM.
Expand Down
33 changes: 31 additions & 2 deletions tools/check-docs-truth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ Deno.test('docs-truth current: VNode is flagged on prose surfaces but not in doc
);
});

Deno.test('docs-truth current: www/app code surfaces are in removed-vocabulary scope (#1260)', () => {
// www/app route data and component code samples must not present removed
// v0.43 authoring vocabulary as current.
const sampleDrift = "import { defineElement } from '@openelement/element';\n" +
'const list = `<For each={items}>`;\n';
const componentNames = findRemovedAuthoringVocabulary(
'www/app/components/page-home.tsx',
sampleDrift,
);
assert(
componentNames.includes('removed defineElement authoring helper'),
componentNames.join(','),
);
assert(componentNames.includes('removed For control-flow factory'), componentNames.join(','));
const routeNames = findRemovedAuthoringVocabulary(
'www/app/routes/apilist.tsx',
"defineIsland('x-island', () => null);",
);
assert(routeNames.includes('removed defineIsland() authoring helper'), routeNames.join(','));
});

Deno.test('docs-truth current: lookalikes and out-of-scope surfaces are not flagged', () => {
// defineIslandConfig stays: only the removed defineIsland( call is matched.
assertEquals(
Expand All @@ -64,9 +85,17 @@ Deno.test('docs-truth current: lookalikes and out-of-scope surfaces are not flag
),
[],
);
// www/app sources carry their own www gate and are out of scope here.
assertEquals(
findRemovedAuthoringVocabulary('www/app/routes/apilist.tsx', 'defineElement'),
findRemovedAuthoringVocabulary(
'www/app/components/page-home.tsx',
"@element('index-index') class PageHome extends OpenElement",
),
[],
);
// Generated www/app content-data mirrors are build artifacts of the gated
// www/content sources and stay out of scope (#1260).
assertEquals(
findRemovedAuthoringVocabulary('www/app/data/_generated-guide-data.ts', 'defineElement'),
[],
);
});
Expand Down
23 changes: 19 additions & 4 deletions tools/check-docs-truth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,14 +237,26 @@ const REMOVED_AUTHORING: Array<{ re: RegExp; name: string }> = [
const REMOVED_VNODE = { re: /\bVNode\b/u, name: 'removed VNode runtime vocabulary' };

/**
* The removed-authoring scan covers the current-truth docs set plus the
* website content collections and package/root READMEs. www/app sources are
* deliberately out of scope here (they carry their own www gate).
* www/app hand-authored code surfaces (#1260): route data and component code
* samples must not present removed v0.43 vocabulary as current. Generated
* content-data mirrors (www/app/data/_generated-*) are excluded — they are
* build artifacts of the gated www/content sources and carry allow-listed
* migration history verbatim.
*/
function isWwwAppCodeSurface(file: string): boolean {
return file.startsWith('www/app/') && !file.includes('/data/_generated-');
}

/**
* The removed-authoring scan covers the current-truth docs set, the website
* content collections, hand-authored www/app code surfaces and package/root
* READMEs.
*/
export function removedAuthoringApplies(file: string): boolean {
return file.startsWith('docs/') ||
file.startsWith('www/content/guide/') ||
file.startsWith('www/content/architecture/') ||
isWwwAppCodeSurface(file) ||
/^packages\/[^/]+\/README\.md$/u.test(file) ||
file === 'README.md' ||
file === 'README.zh.md';
Expand Down Expand Up @@ -285,6 +297,9 @@ const currentCheck: DocsTruthCheck = {
if (!/\.(ts|tsx|md)$/.test(file) || CURRENT_DOC_ALLOWED.some((a) => file.includes(a))) {
return;
}
// Generated www/app content-data mirrors are build artifacts of the
// gated www/content sources; scan the sources, not the mirrors (#1260).
if (file.includes('www/app/data/_generated-')) return;
const text = await Deno.readTextFile(file);
const removedVocab = removedAuthoringApplies(file);
const removedVnode = removedVnodeApplies(file);
Expand All @@ -311,7 +326,7 @@ const currentCheck: DocsTruthCheck = {

for (
const dir of [
'www/app/routes',
'www/app',
'docs',
'www/content/guide',
'www/content/architecture',
Expand Down
18 changes: 12 additions & 6 deletions www/app/components/page-home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,19 @@ export default class PageHome extends OpenElement {
</div>
<div class='scene-art'>
<open-code-block>
<pre><code>{`import { defineElement } from '@openelement/element'
<pre><code>{`import { element, OpenElement, property } from '@openelement/element'

export const Counter = defineElement('open-counter', {
render: (props) => (
<button type="button">Count: {props.count ?? 0}</button>
),
})
@element('open-counter', { root: 'shadow-open' })
export class OpenCounter extends OpenElement {
@property({ reflect: true, attribute: 'count', type: Number })
count = 0

render() {
return (
<button type="button">Count: {this.count}</button>
)
}
}

// SSR: <open-counter count="0"> + DSD shadow root.
// No JavaScript required for first paint.`}</code></pre>
Expand Down
12 changes: 6 additions & 6 deletions www/app/routes/apilist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ const packages: ApiPackage[] = [
notes: {
en: [
'Start here for standalone element authoring.',
'Use `defineElement`, `OpenElement`, `StyleSheet` and signal helpers without importing renderer internals.',
'Keyed lists: `<For each={items} key={fn}>{(item) => ...}</For>` is exported from the root.',
'Author compiled elements as `@element`-decorated `OpenElement` classes with `@property` state; `StyleSheet` and signal helpers come from the same root.',
'@experimental additions: the `element`/`property` decorator intrinsics (#1209) and the dangerous-key guards `isDangerousKey`, `injectPropsSafe`, `DANGEROUS_KEYS` (#1214).',
],
zh: [
'独立的元素创作从这里开始。',
'直接使用 `defineElement`、`OpenElement`、`StyleSheet` 与 signal 辅助函数,无需引入 renderer 内部实现。',
'键控列表:`<For each={items} key={fn}>{(item) => ...}</For>` 从包根导出。',
'以 `@element` 装饰的 `OpenElement` 类和 `@property` 状态创作编译元素;`StyleSheet` 与 signal 辅助函数同出包根。',
'@experimental 新增:`element`/`property` 装饰器内在量(#1209)与危险键守卫 `isDangerousKey`、`injectPropsSafe`、`DANGEROUS_KEYS`(#1214)。',
],
},
kind: 'core',
Expand All @@ -62,11 +62,11 @@ const packages: ApiPackage[] = [
internalExports: ['i18n'],
notes: {
en: [
'Use `definePage`, `defineIsland` and `defineApp` for application authoring.',
'Use `definePage`, `defineIslandConfig` and `defineApp` for application authoring.',
'The router and request-driver implementation are internal product knowledge.',
],
zh: [
'用 `definePage`、`defineIsland` 与 `defineApp` 进行应用创作。',
'用 `definePage`、`defineIslandConfig` 与 `defineApp` 进行应用创作。',
'router 与请求驱动的实现属于产品内部知识。',
],
},
Expand Down
Loading