npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jsp-template-compiler

v1.0.4

Published

前端模板引擎,支持构建时编译,生成纯原生 JavaScript 代码

Readme

jsp-template-compiler

前端模板引擎,支持构建时编译,生成纯原生 JavaScript 代码。

特性

  • 🚀 构建时编译 - 模板语法在构建时转换为原生 JS,运行时零开销
  • 🔄 自动响应式 - 函数执行后自动重新渲染页面
  • 📦 零依赖 - 生成的 HTML 不需要引入任何外部库
  • 🎯 简洁语法 - 类似 Mustache/EJS 的模板语法
  • 👀 监听模式 - 支持文件变化自动编译
  • 🧩 组件化开发 - 支持三段式组件(template/style/script)
  • 🎨 Scoped 样式 - 组件样式隔离,避免样式污染

安装

# 全局安装
npm install -g jsp-template-compiler

# 或作为项目依赖
npm install jsp-template-compiler --save-dev

快速开始

1. 创建模板文件

创建一个 .jsp 文件(例如 index.jsp):

<div>
    <h1>{{ title }}</h1>
    {% for (let i = 0; i < list.length; i++) { %}
        <p>{{ list[i] }}</p>
    {% } %}
    <button onclick="addItem()">添加</button>
</div>

<script>
    let title = 'Hello World'
    let list = ['Item 1', 'Item 2', 'Item 3']

    const addItem = () => {
        list.push('Item ' + (list.length + 1))
        // 无需手动调用 render(),会自动注入
    }
</script>

<style>
    h1 { color: blue; }
</style>

2. 编译

# 编译当前目录
jsp-build

# 编译指定目录
jsp-build src

# 输出到指定目录
jsp-build -o dist

# 监听模式
jsp-build -w

3. 查看结果

打开生成的 .html 文件即可运行。

命令行选项

用法:
  jsp-build [选项] [目录]

选项:
  -h, --help      显示帮助信息
  -v, --version   显示版本号
  -w, --watch     监听文件变化自动编译
  -o, --output    指定输出目录

示例:
  jsp-build                 # 编译当前目录
  jsp-build src             # 编译 src 目录
  jsp-build -o dist         # 输出到 dist 目录
  jsp-build -w              # 监听模式
  jsp-build src -o dist -w  # 组合使用

模板语法

变量插值 {{ expression }}

输出表达式的值:

<h1>{{ title }}</h1>
<p>{{ user.name }}</p>
<span>{{ count + 1 }}</span>
<div class="{{ isActive ? 'active' : '' }}">...</div>

组件引入 {% include "path" %}

引入其他 JSP 文件作为组件,支持三段式结构自动聚合

<!-- 主文件 -->
{% include "components/header" %}

<div>主要内容</div>

{% include "components/footer" %}

<script>
    let title = '我的网站'
</script>

组件写法(两种都支持):

A. 三段式(推荐):

<!-- components/header.jsp -->
<template>
    <header class="header">
        <h1>{{ title }}</h1>
    </header>
</template>

<style scoped>
    .header {
        background: #333;
        color: white;
        padding: 20px;
    }
    .header h1 {
        margin: 0;
    }
</style>

<script>
    // 组件自己的逻辑(可选)
    console.log('Header component loaded')
</script>

B. 兼容旧写法:

<!-- components/footer.jsp -->
<footer class="footer">
    <p>© 2024 {{ companyName }}</p>
</footer>

<style>
    .footer { text-align: center; }
</style>

路径规则:

  • 相对路径:相对于当前 JSP 文件
  • 绝对路径:从项目根目录开始
  • 自动添加 .jsp 扩展名(如果未指定)

Scoped 样式:

  • 使用 <style scoped> 实现样式隔离
  • 自动为组件根节点添加 data-c="xxx" 属性
  • CSS 选择器自动添加作用域前缀
  • 避免样式污染页面

自动聚合:

  • 所有组件的样式自动聚合到 <style id="components-css">
  • 所有组件的脚本自动聚合到页面脚本中
  • 页面样式单独放在 <style id="page-css">
  • 输出结构清晰,便于调试

注意:

  • 支持嵌套 include(组件中可以包含其他组件)
  • 自动检测循环引用并报错
  • 组件样式和脚本会与页面样式和脚本合并

代码块 {% code %}

执行 JavaScript 代码(循环、条件等):

<!-- 循环 -->
{% for (let i = 0; i < list.length; i++) { %}
    <p>{{ list[i] }}</p>
{% } %}

<!-- 条件判断 -->
{% if (show) { %}
    <div>显示内容</div>
{% } else { %}
    <div>隐藏内容</div>
{% } %}

<!-- forEach -->
{% list.forEach(item => { %}
    <p>{{ item.name }}</p>
{% }); %}

JSP 文件结构

页面文件(主文件)

<!-- HTML 模板部分 -->
<div>
    {{ variable }}
    {% code %}
    {% include "components/header" %}
</div>

<!-- 数据和逻辑部分 -->
<script>
    // 数据定义(顶层变量会自动注入到模板作用域)
    let list = [...]
    
    // 函数定义(执行后自动重新渲染)
    const fetchData = async () => {
        list = await fetch('/api').then(r => r.json())
    }
</script>

<!-- 样式部分(可选)-->
<style>
    .container { ... }
</style>

组件文件(三段式,推荐)

<!-- 模板部分 -->
<template>
    <div class="component">
        <h2>{{ title }}</h2>
        <p>{{ content }}</p>
    </div>
</template>

<!-- 样式部分(scoped 实现样式隔离)-->
<style scoped>
    .component {
        padding: 20px;
        border: 1px solid #ddd;
    }
    .component h2 {
        color: #333;
    }
</style>

<!-- 脚本部分(可选)-->
<script>
    // 组件自己的逻辑
    console.log('Component loaded')
</script>

编译后的输出结构:

<head>
    <!-- 组件样式(自动聚合) -->
    <style id="components-css">
        /* component style (scoped) from: components/header.jsp */
        [data-c="abc123"] .header { ... }
        [data-c="def456"] .footer { ... }
    </style>
    
    <!-- 页面样式 -->
    <style id="page-css">
        .container { ... }
    </style>
</head>
<body>
    <div id="app"></div>
    <script>
        // 组件脚本(自动聚合)
        // component script from: components/header.jsp
        ...
        
        // 页面脚本
        // page script
        ...
        
        // 渲染函数
        function render() { ... }
        
        // 初始渲染
        render()
    </script>
</body>

自动响应式

构建工具会自动在所有函数末尾注入 render() 调用:

编译前:

const updateData = () => {
    list = newData
}

编译后:

const updateData = () => {
    list = newData
    render()  // ← 自动注入
}

编程式使用

const { compile } = require('jsp-template-compiler');

const jspContent = `
<div>{{ message }}</div>
<script>
    let message = 'Hello'
</script>
`;

const html = compile(jspContent, 'Page Title');
console.log(html);

API

compile(jspContent, title)

编译 JSP 内容为 HTML。

  • jspContent - JSP 文件内容字符串
  • title - 页面标题(可选,默认 'Page')
  • 返回编译后的 HTML 字符串

compileTemplate(templateStr, varNames)

编译模板字符串为渲染函数代码。

autoInjectRender(scriptCode)

在脚本代码中的函数末尾自动注入 render() 调用。

extractVarNames(scriptCode)

从脚本代码中提取顶层变量名。

示例

组件化开发(三段式 + Scoped 样式)

主文件 (index.jsp):

{% include "components/header" %}

<main class="main">
    <h2>欢迎</h2>
    <p>{{ message }}</p>
</main>

{% include "components/footer" %}

<script>
    let message = 'Hello World'
    let title = '我的网站'
    let companyName = 'Acme Inc'
</script>

<style>
    .main {
        max-width: 800px;
        margin: 0 auto;
        padding: 20px;
    }
</style>

组件 (components/header.jsp) - 三段式:

<template>
    <header class="header">
        <h1>{{ title }}</h1>
        <nav>
            <a href="/">首页</a>
            <a href="/about">关于</a>
        </nav>
    </header>
</template>

<style scoped>
    .header {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        padding: 20px;
    }
    .header h1 {
        margin: 0;
        font-size: 2rem;
    }
    .header nav a {
        color: white;
        margin-right: 15px;
    }
</style>

<script>
    console.log('Header component loaded')
</script>

组件 (components/footer.jsp) - 兼容旧写法:

<footer class="footer">
    <p>© 2024 {{ companyName }}. All rights reserved.</p>
</footer>

<style>
    .footer {
        text-align: center;
        padding: 20px;
        border-top: 1px solid #eee;
    }
</style>

编译后的效果:

  • 组件样式自动添加 scoped 前缀:[data-c="abc123"] .header { ... }
  • 组件根节点自动添加 data-c 属性:<header data-c="abc123" class="header">
  • 所有组件样式聚合到 <style id="components-css">
  • 所有组件脚本聚合到页面脚本中
  • 页面样式单独在 <style id="page-css">

列表渲染

<ul>
    {% for (let item of items) { %}
        <li>{{ item.name }} - {{ item.price }}元</li>
    {% } %}
</ul>

<script>
    let items = [
        { name: '苹果', price: 5 },
        { name: '香蕉', price: 3 }
    ]
</script>

条件渲染

{% if (isLogin) { %}
    <p>欢迎,{{ username }}</p>
{% } else { %}
    <button onclick="login()">登录</button>
{% } %}

<script>
    let isLogin = false
    let username = ''

    const login = () => {
        isLogin = true
        username = '张三'
    }
</script>

异步数据

<button onclick="fetchData()">加载数据</button>
<ul>
    {% for (let item of dataList) { %}
        <li>{{ item.title }}</li>
    {% } %}
</ul>

<script>
    let dataList = []

    const fetchData = async () => {
        const res = await fetch('/api/data')
        dataList = await res.json()
    }
</script>

注意事项

  1. 变量必须先定义 - 模板中使用的变量必须在 <script> 中用 let/const/var 声明
  2. 函数自动注入 render - 所有函数执行后都会自动重新渲染
  3. 顶层变量 - 只有顶层作用域的变量会被注入到模板中,函数内部的局部变量不会
  4. 组件样式隔离 - 使用 <style scoped> 实现样式隔离,避免污染页面
  5. 组件自动聚合 - 组件的样式和脚本会自动聚合,无需手动管理
  6. 根节点注入 - Scoped 样式会自动将 data-c 注入到组件根节点,如果无法注入会降级为包裹方案

License

MIT