Socket
Socket
Sign inDemoInstall

honox

Package Overview
Dependencies
139
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.16 to 0.1.17

dist/server/context-storage.d.ts

3

dist/client/client.d.ts

@@ -15,2 +15,5 @@ import { Hydrate, CreateElement, CreateChildren, TriggerHydration } from '../types.js';

ISLAND_FILES?: Record<string, () => Promise<unknown>>;
/**
* @deprecated
*/
island_root?: string;

@@ -17,0 +20,0 @@ };

10

dist/client/client.js

@@ -11,9 +11,9 @@ import { render } from "hono/jsx/dom";

const FILES = options?.ISLAND_FILES ?? {
...import.meta.glob("/app/islands/**/[a-zA-Z0-9[-]+.(tsx|ts)"),
...import.meta.glob("/app/routes/**/_[a-zA-Z0-9[-]+.island.(tsx|ts)")
...import.meta.glob("/app/islands/**/[a-zA-Z0-9-]+.tsx"),
...import.meta.glob("/app/**/_[a-zA-Z0-9-]+.island.tsx"),
...import.meta.glob("/app/**/$[a-zA-Z0-9-]+.tsx")
};
const root = options?.island_root ?? "/app";
const hydrateComponent = async (document2) => {
const filePromises = Object.keys(FILES).map(async (filePath) => {
const componentName = filePath.replace(root, "");
const componentName = filePath;
const elements = document2.querySelectorAll(

@@ -44,3 +44,3 @@ `[${COMPONENT_NAME}="${componentName}"]:not([data-hono-hydrated])`

createElement,
async (name) => (await FILES[`${root}${name}`]()).default
async (name) => (await FILES[`${name}`]()).default
);

@@ -47,0 +47,0 @@ }

@@ -1,5 +0,5 @@

import { FC } from 'hono/jsx';
declare const HasIslands: ({ children }: {
children: any;
}) => any;
declare const HasIslands: FC;
export { HasIslands };
import { Fragment, jsx } from "hono/jsx/jsx-runtime";
import { useRequestContext } from "hono/jsx-renderer";
import { IMPORTING_ISLANDS_ID } from "../../constants.js";
import { contextStorage } from "../context-storage.js";
const HasIslands = ({ children }) => {
const c = useRequestContext();
return /* @__PURE__ */ jsx(Fragment, { children: c.get(IMPORTING_ISLANDS_ID) ? children : /* @__PURE__ */ jsx(Fragment, {}) });
const c = contextStorage.getStore();
if (!c) {
throw new Error("No context found");
}
return /* @__PURE__ */ jsx(Fragment, { children: c.get(IMPORTING_ISLANDS_ID) && children });
};

@@ -8,0 +11,0 @@ export {

export { HasIslands } from './has-islands.js';
export { Script } from './script.js';
import 'hono/jsx';
import 'vite';

@@ -1,2 +0,1 @@

import { FC } from 'hono/jsx';
import { Manifest } from 'vite';

@@ -11,4 +10,4 @@

};
declare const Script: FC<Options>;
declare const Script: (options: Options) => any;
export { Script };
import { Fragment, jsx } from "hono/jsx/jsx-runtime";
import { HasIslands } from "./has-islands.js";
const Script = async (options) => {
const Script = (options) => {
const src = options.src;

@@ -5,0 +5,0 @@ if (options.prod ?? import.meta.env.PROD) {

@@ -8,3 +8,2 @@ export { createApp } from './with-defaults.js';

import '../constants.js';
import 'hono/jsx';
import 'vite';
import { Hono } from "hono";
import { createMiddleware } from "hono/factory";
import { IMPORTING_ISLANDS_ID } from "../constants.js";
import { contextStorage } from "./context-storage.js";
import {

@@ -9,3 +10,3 @@ filePathToPath,

sortDirectoriesByDepth
} from "../utils/file.js";
} from "./utils/file.js";
const NOTFOUND_FILENAME = "_404.tsx";

@@ -20,2 +21,5 @@ const ERROR_FILENAME = "_error.tsx";

const trailingSlash = options.trailingSlash ?? false;
app.use(async function ShareContext(c, next) {
await contextStorage.run(c, () => next());
});
if (options.init) {

@@ -22,0 +26,0 @@ options.init(app);

import { Plugin } from 'vite';
declare function injectImportingIslands(): Promise<Plugin>;
type InjectImportingIslandsOptions = {
appDir?: string;
islandDir?: string;
};
declare function injectImportingIslands(options?: InjectImportingIslandsOptions): Promise<Plugin>;
export { injectImportingIslands };

@@ -8,9 +8,11 @@ import { readFile } from "fs/promises";

import { IMPORTING_ISLANDS_ID } from "../constants.js";
import { matchIslandComponentId } from "./utils/path.js";
const generate = _generate.default ?? _generate;
async function injectImportingIslands() {
const isIslandRegex = new RegExp(/(\/islands\/|\_[a-zA-Z0-9[-]+\.island\.[tj]sx$)/);
const fileRegex = new RegExp(/(routes|_renderer|_error|_404)\/.*\.[tj]sx$/);
async function injectImportingIslands(options) {
let appPath = "";
const islandDir = options?.islandDir ?? "/app/islands";
let root = "";
const cache = {};
const walkDependencyTree = async (baseFile, dependencyFile) => {
const depPath = dependencyFile ? path.join(path.dirname(baseFile), dependencyFile) + ".tsx" : baseFile;
const walkDependencyTree = async (baseFile, resolve, dependencyFile) => {
const depPath = dependencyFile ? typeof dependencyFile === "string" ? path.join(path.dirname(baseFile), dependencyFile) + ".tsx" : dependencyFile["id"] : baseFile;
const deps = [depPath];

@@ -25,3 +27,6 @@ try {

const childDeps = await Promise.all(
currentFileDeps.map(async (x) => await walkDependencyTree(depPath, x))
currentFileDeps.map(async (file) => {
const resolvedId = await resolve(file, baseFile);
return await walkDependencyTree(depPath, resolve, resolvedId ?? file);
})
);

@@ -36,7 +41,16 @@ deps.push(...childDeps.flat());

name: "inject-importing-islands",
configResolved: async (config) => {
appPath = path.join(config.root, options?.appDir ?? "/app");
root = config.root;
},
async transform(sourceCode, id) {
if (!fileRegex.test(id)) {
if (!path.resolve(id).startsWith(appPath)) {
return;
}
const hasIslandsImport = (await walkDependencyTree(id)).flat().some((x) => isIslandRegex.test(normalizePath(x)));
const hasIslandsImport = (await Promise.all(
(await walkDependencyTree(id, async (id2) => await this.resolve(id2))).flat().map(async (x) => {
const rootPath = "/" + path.relative(root, normalizePath(x)).replace(/\\/g, "/");
return matchIslandComponentId(rootPath, islandDir);
})
)).some((matched) => matched);
if (!hasIslandsImport) {

@@ -43,0 +57,0 @@ return;

@@ -6,3 +6,7 @@ import { Plugin } from 'vite';

type IslandComponentsOptions = {
/**
* @deprecated
*/
isIsland?: IsIsland;
islandDir?: string;
reactApiImportSource?: string;

@@ -9,0 +13,0 @@ };

@@ -32,5 +32,3 @@ import fs from "fs/promises";

import { parse as parseJsonc } from "jsonc-parser";
function isComponentName(name) {
return /^[A-Z][A-Z0-9]*[a-z][A-Za-z0-9]*$/.test(name);
}
import { matchIslandComponentId, isComponentName } from "./utils/path.js";
function addSSRCheck(funcName, componentName, componentExport) {

@@ -162,2 +160,3 @@ const isSSR = memberExpression(

let reactApiImportSource = options?.reactApiImportSource;
const islandDir = options?.islandDir ?? "/app/islands";
return {

@@ -182,3 +181,3 @@ name: "transform-island-components",

async load(id) {
if (/\/honox\/.*?\/vite\/components\//.test(id)) {
if (/\/honox\/.*?\/(?:server|vite)\/components\//.test(id)) {
if (!reactApiImportSource) {

@@ -193,11 +192,4 @@ return;

}
const defaultIsIsland = (id2) => {
const islandDirectoryPath = path.join(root, "app");
return path.resolve(id2).startsWith(islandDirectoryPath);
};
const matchIslandPath = options?.isIsland ?? defaultIsIsland;
if (!matchIslandPath(id)) {
return;
}
const match = id.match(/(\/islands\/.+?\.tsx$)|(\/routes\/.*\_[a-zA-Z0-9[-]+\.island\.tsx$)/);
const rootPath = "/" + path.relative(root, id).replace(/\\/g, "/");
const match = matchIslandComponentId(rootPath, islandDir);
if (match) {

@@ -204,0 +196,0 @@ const componentName = match[0];

@@ -0,3 +1,5 @@

import fs from "fs";
import os from "os";
import path from "path";
import { transformJsxTags, islandComponents } from "./island-components";
import { transformJsxTags, islandComponents } from "./island-components.js";
describe("transformJsxTags", () => {

@@ -218,20 +220,44 @@ it("Should add component-wrapper and component-name attribute", () => {

describe("reactApiImportSource", () => {
const component = path.resolve(__dirname, "../vite/components/honox-island.tsx").replace(/\\/g, "/");
it("use 'hono/jsx' by default", async () => {
const plugin = islandComponents();
await plugin.configResolved({ root: "root" });
const res = await plugin.load(component);
expect(res.code).toMatch(/'hono\/jsx'/);
expect(res.code).not.toMatch(/'react'/);
describe("vite/components", () => {
const component = path.resolve(__dirname, "../vite/components/honox-island.tsx").replace(/\\/g, "/");
it("use 'hono/jsx' by default", async () => {
const plugin = islandComponents();
await plugin.configResolved({ root: "root" });
const res = await plugin.load(component);
expect(res.code).toMatch(/'hono\/jsx'/);
expect(res.code).not.toMatch(/'react'/);
});
it("enable to specify 'react'", async () => {
const plugin = islandComponents({
reactApiImportSource: "react"
});
await plugin.configResolved({ root: "root" });
const res = await plugin.load(component);
expect(res.code).not.toMatch(/'hono\/jsx'/);
expect(res.code).toMatch(/'react'/);
});
});
it("enable to specify 'react'", async () => {
const plugin = islandComponents({
reactApiImportSource: "react"
describe("server/components", () => {
const tmpdir = os.tmpdir();
const component = path.resolve(tmpdir, "honox/dist/server/components/has-islands.js").replace(/\\/g, "/");
fs.mkdirSync(path.dirname(component), { recursive: true });
fs.writeFileSync(component, "import { jsx } from 'hono/jsx/jsx-runtime'");
it("use 'hono/jsx' by default", async () => {
const plugin = islandComponents();
await plugin.configResolved({ root: "root" });
const res = await plugin.load(component);
expect(res.code).toMatch(/'hono\/jsx\/jsx-runtime'/);
expect(res.code).not.toMatch(/'react\/jsx-runtime'/);
});
await plugin.configResolved({ root: "root" });
const res = await plugin.load(component);
expect(res.code).not.toMatch(/'hono\/jsx'/);
expect(res.code).toMatch(/'react'/);
it("enable to specify 'react'", async () => {
const plugin = islandComponents({
reactApiImportSource: "react"
});
await plugin.configResolved({ root: "root" });
const res = await plugin.load(component);
expect(res.code).not.toMatch(/'hono\/jsx\/jsx-runtime'/);
expect(res.code).toMatch(/'react\/jsx-runtime'/);
});
});
});
});
{
"name": "honox",
"version": "0.1.16",
"version": "0.1.17",
"main": "dist/index.js",

@@ -115,3 +115,3 @@ "type": "module",

"@babel/types": "^7.23.6",
"@hono/vite-dev-server": "^0.12.0",
"@hono/vite-dev-server": "^0.12.1",
"jsonc-parser": "^3.2.1",

@@ -132,3 +132,3 @@ "precinct": "^12.0.2"

"glob": "^10.3.10",
"hono": "^4.3.2",
"hono": "^4.3.4",
"np": "7.7.0",

@@ -135,0 +135,0 @@ "prettier": "^3.1.1",

@@ -316,3 +316,3 @@ # HonoX

If you want to add a `nonce` attribute to `<Script />`, `<script />`, or `<style />` element, you can use [Security Headers Middleware](https://hono.dev/middleware/builtin/secure-headers).
If you want to add a `nonce` attribute to `<Script />` or `<script />` element, you can use [Security Headers Middleware](https://hono.dev/middleware/builtin/secure-headers).

@@ -326,10 +326,9 @@ Define the middleware:

export default createRoute(
secureHeaders({
contentSecurityPolicy: {
scriptSrc: [NONCE],
styleSrc: [NONCE],
},
})
)
secureHeaders({
contentSecurityPolicy: import.meta.env.PROD
? {
scriptSrc: [NONCE],
}
: undefined,
})
```

@@ -371,3 +370,3 @@

- Placed under `app/islands` directory or named with `_` prefix and `island.tsx` suffix like `_componentName.island.tsx`.
- Placed under `app/islands` directory or named with `$` prefix like `$componentName.tsx`.
- It should be exported as a `default` or a proper component name that uses camel case but does not contain `_` and is not all uppercase.

@@ -754,3 +753,4 @@

name = "my-project-name"
compatibility_date = "2023-12-01"
compatibility_date = "2024-04-01"
compatibility_flags = [ "nodejs_compat" ]
pages_build_output_dir = "./dist"

@@ -790,2 +790,12 @@

Add the `wrangler.toml`:
```toml
# wrangler.toml
name = "my-project-name"
compatibility_date = "2024-04-01"
compatibility_flags = [ "nodejs_compat" ]
pages_build_output_dir = "./dist"
```
Setup the `vite.config.ts`:

@@ -835,3 +845,3 @@

```txt
wrangler pages deploy ./dist
wrangler pages deploy
```

@@ -838,0 +848,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc