fryweb


Namefryweb JSON
Version 0.3.4 PyPI version JSON
download
home_pageNone
SummaryA asgi/wsgi server that can generate HTML, Javascript and CSS, based on fry file
upload_time2024-12-14 03:41:27
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseMIT
keywords asgi fry fryweb server wsgi
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Fryweb
A Python library to generate HTML, Javascript and CSS, based on .fry file.

Fry is jsx in python, it's the core of this project.

Fryweb is heavily inspired by React JSX, TailwindCSS, WindiCSS in JS ecosystem.

## Features
* Support fry extension to normal python file, similar to jsx, write html tags in python file.
* Provide a fry loader for python import machanism, load and execute .fry files directly by CPython.
* Provide a utility-first css framework, similar to TailwindCSS, support attributify mode similar to WindiCSS.
* Support wsgi/asgi application.
* Provide pygments lexer for fry.
* Provide a development server which supports server/browser auto reloading when file saved.
* Provide a command line tool `fry`, build css/js, highlight and run fry file and run development server. 
* Support plugin machanism, anyone can extends with her/his own custom css utilities.

All features are implemented in pure Python, no node.js ecosystem is required.

## Installation

```bash
$ pip install fryweb
```

## Usage

### 1. Basic
create app.fry file:

```python
from fryweb import html, Element
from flask import Flask

app = Flask(__name__)

def App():
    <template>
      <h1 text-cyan-500 hover:text-cyan-600 text-center mt-100px>
        Hello FryWEB!
      </h1>
    </template>

@app.get('/')
def index():
    return html(App, "Hello")
```

in the same directory as app.fry, run command:

```bash
$ fry topy app.fry
```

check the generated python content:

```python
from fryweb import html, Element
from flask import Flask

app = Flask(__name__)

def App():
    return Element("h1", {"class": "text-cyan-500 hover:text-cyan-600 text-center mt-100px", "children": ["Hello FryWEB!"]})

@app.get('/')
def index():
    return html(App, "Hello")

```

To generate CSS file `static/css/styles.css`, run command:
```bash
$ fry tocss app.fry
```

Generated CSS:

```css
....

.text-cyan-500 {
  color: rgb(6 182 212);
}

.text-center {
  text-align: center;
}

.mt-100px {
  margin-top: 100px;
}

.hover\:text-cyan-600:hover {
  color: rgb(8 145 178);
}

```

To serve this app, run command:

```bash
$ fry dev 
```

Open browser, access `http://127.0.0.1:5000` to browse the page.

Change the app.fry file, save, check the browser auto reloading.

`fryweb.render` can be used to render component directly.

Create components.fry and input following code:

```python
from fryweb import Element

def Component(**props):
    <template>
      <h1 text-cyan-500 hover:text-cyan-600 text-center mt-100px>
        Hello Fryweb!
      </h1>
    </template>

if __name__ == '__main__':
    from fryweb import render
    print(render(Component))
```

Run command to see the generated html fragment:
```bash
$ fry run component.fry
```


### 2. Using python variable in html markup:

```python
from fryweb import html, Element
from flask import Flask

app = Flask(__name__)

def App():
    initial_count = 10

    <template>
      <div>
        <h1 text-cyan-500 hover:text-cyan-600 text-center mt-100px>
          Hello Fryweb!
        </h1>
        <p text-indigo-600 text-center mt-9>Count: {initial_count}</p>
      </div>
    </template>

@app.get('/')
def index():
    return html(App, "Hello")
```

Generated python:

```python
from fryweb import html, Element
from flask import Flask

app = Flask(__name__)

def App():
    initial_count = 10
    return Element("div", {"children": [Element("h1", {"class": "text-cyan-500 hover:text-cyan-600 text-center mt-100px", "children": ["Hello Fryweb!"]}), Element("p", {"class": "text-indigo-600 text-center mt-9", "children": ["Count:", (initial_count)]})]})

@app.get('/')
def index():
    return html(App, "Hello")

```

### 3. Add js logic and reactive variable(signal/computed):

```python
from fryweb import html, Element
from flask import Flask

app = Flask(__name__)

def App():
    initial_count = 20

    <template>
       <div>
         <h1 ref=(header) text-cyan-500 hover:text-cyan-600 text-center mt-100px>
           Hello Fryweb!
         </h1>
         <p text-indigo-600 text-center mt-9>
           Count:
           <span text-red-600>[{initial_count}](count)</span>
         </p>
         <p text-indigo-600 text-center mt-9>
           Double:
           <span text-red-600>[{initial_count*2}](doubleCount)</span>
         </p>
         <div flex w-full justify-center>
           <button
             @click=(increment)
             class="inline-flex items-center justify-center h-10 gap-2 px-5 text-sm font-medium tracking-wide text-white transition duration-300 rounded focus-visible:outline-none whitespace-nowrap bg-emerald-500 hover:bg-emerald-600 focus:bg-emerald-700 disabled:cursor-not-allowed disabled:border-emerald-300 disabled:bg-emerald-300 disabled:shadow-none">
             Increment
           </button>
         </div>
       </div>
    </template>

    <script initial={initial_count}>
       import {signal, computed} from "fryweb"
 
       let count = signal(initial)
 
       let doubleCount = computed(()=>count.value*2)
 
       function increment() {
           count.value ++;
           header.textContent = `Hello Fryweb(${count.value})`;
       }
    </script>


@app.get('/')
def index():
    return html(App, "Hello")
```

Generated python:

```python
from fryweb import html, Element
from flask import Flask

app = Flask(__name__)

def App():
    initial_count = 20

    return Element("div", {"call-client-script": ["App-1171022438ea1f5e3d31f5fb191ca3c18adfda49", [("initial", (initial_count))]], "children": [Element("h1", {"ref:header": Element.ClientEmbed(0), "class": "text-cyan-500 hover:text-cyan-600 text-center mt-100px", "children": ["Hello Fryweb!"]}), Element("p", {"class": "text-indigo-600 text-center mt-9", "children": ["Count:", Element("span", {"class": "text-red-600", "children": [Element("span", {"*": Element.ClientEmbed(1), "children": [f"""{initial_count}"""]})]})]}), Element("p", {"class": "text-indigo-600 text-center mt-9", "children": ["Double:", Element("span", {"class": "text-red-600", "children": [Element("span", {"*": Element.ClientEmbed(2), "children": [f"""{initial_count*2}"""]})]})]}), Element("div", {"class": "flex w-full justify-center", "children": [Element("button", {"@click": Element.ClientEmbed(3), "class": "inline-flex items-center justify-center h-10 gap-2 px-5 text-sm font-medium tracking-wide text-white transition duration-300 rounded focus-visible:outline-none whitespace-nowrap bg-emerald-500 hover:bg-emerald-600 focus:bg-emerald-700 disabled:cursor-not-allowed disabled:border-emerald-300 disabled:bg-emerald-300 disabled:shadow-none", "children": ["Increment"]})]})]})


@app.get('/')
def index():
    return html(App, "Hello")
```

Generated js script `static/js/components/.tmp/App-1171022438ea1f5e3d31f5fb191ca3c18adfda49.js`:

```js
export { hydrate as hydrateAll } from "fryweb";
export const hydrate = async function (element$$, doHydrate$$) {
    const { header, initial } = element$$.fryargs;

              const {signal, computed} = await import("fryweb")

              let count = signal(initial)

              let doubleCount = computed(()=>count.value*2)

              function increment() {
                  count.value ++;
                  header.textContent = `Hello Fryweb(${count.value})`;
              }

    const embeds$$ = [header, count, doubleCount, increment];
    doHydrate$$(element$$, embeds$$);
};
```

Generated HTML:

```html
<!DOCTYPE html>
<html lang=en>
  <head>
    <meta charset="utf-8">
    <title>Hello</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="/static/css/styles.css">
  </head>
  <body>
    <div><script data-fryid="1" data-fryclass="app:App" data-initial="20"></script><h1 class="text-cyan-500 hover:text-cyan-600 text-center mt-100px" data-fryembed="1/0-ref-header">Hello Fryweb!</h1><p class="text-indigo-600 text-center mt-9">Count:<span class="text-red-600"><span data-fryembed="1/1-text">20</span></span></p><p class="text-indigo-600 text-center mt-9">Double:<span class="text-red-600"><span data-fryembed="1/2-text">40</span></span></p><div class="flex w-full justify-center"><button class="inline-flex items-center justify-center h-10 gap-2 px-5 text-sm font-medium tracking-wide text-white transition duration-300 rounded focus-visible:outline-none whitespace-nowrap bg-emerald-500 hover:bg-emerald-600 focus:bg-emerald-700 disabled:cursor-not-allowed disabled:border-emerald-300 disabled:bg-emerald-300 disabled:shadow-none" data-fryembed="1/3-event-click">Increment</button></div></div>

    <script type="module">
      let hydrates = {};
      import { hydrate as hydrate_0, hydrateAll } from '/static/js/components/1171022438ea1f5e3d31f5fb191ca3c18adfda49.js';
      hydrates['1'] = hydrate_0;
      await hydrateAll(hydrates);
    </script>

  <script type="module">
    let serverId = null;
    let eventSource = null;
    let timeoutId = null;
    function checkAutoReload() {
        if (timeoutId !== null) clearTimeout(timeoutId);
        timeoutId = setTimeout(checkAutoReload, 1000);
        if (eventSource !== null) eventSource.close();
        eventSource = new EventSource("/_check_hotreload");
        eventSource.addEventListener('open', () => {
            console.log(new Date(), "Auto reload connected.");
            if (timeoutId !== null) clearTimeout(timeoutId);
            timeoutId = setTimeout(checkAutoReload, 1000);
        });
        eventSource.addEventListener('message', (event) => {
            const data = JSON.parse(event.data);
            if (serverId === null) {
                serverId = data.serverId;
            } else if (serverId !== data.serverId) {
                if (eventSource !== null) eventSource.close();
                if (timeoutId !== null) clearTimeout(timeoutId);
                location.reload();
                return;
            }
            if (timeoutId !== null) clearTimeout(timeoutId);
            timeoutId = setTimeout(checkAutoReload, 1000);
        });
    }
    checkAutoReload();
  </script>

  </body>
</html>
```

### 4. Reference html element and component element in js logic:

```python
from fryweb import Element, html
from flask import Flask

app = Flask(__name__)

@app.get('/')
def index():
    return html(RefApp, title="test ref")

def Refed():
    <template>
      <div>
        hello world
      </div>
    </template>
    <script>
      export default {
          hello() {
              console.log('hello hello')
          }
      }
    </script>

def RefApp():

    <template>
      <div w-full h-100vh flex flex-col gap-y-10 justify-center items-center>
        <p ref=(foo) text-indigo-600 text-6xl transition-transform duration-1500>
          Hello World!
        </p>
        <p ref=(bar) text-cyan-600 text-6xl transition-transform duration-1500>
          Hello Fryweb!
        </p>
        {<p refall=(foobar)>foobar</p> for i in range(3)}
        <Refed ref=(refed) refall=(refeds)/>
        {<Refed refall=(refeds) /> for i in range(2)}
      </div>
    </template>

    <script foo bar foobar refed refeds>
      setTimeout(()=>{
        foo.style.transform = "skewY(180deg)";
      }, 1000);
      setTimeout(()=>{
        bar.style.transform = "skewY(180deg)";
      }, 2500);
      for (const fb of foobar) {
        console.log(fb);
      }
      refed.hello()
      for (const r of refeds) {
          r.hello()
      }
    </script>

if __name__ == '__main__':
    print(html(RefApp))
```

## Command Line Tool `fry`

## Configuration

## Django Integration

## Flask Integration

## FastAPI Integration

## License
MIT License

## Road Map
* [ ] support component fetch from backend
* [ ] support component CRUD from frontend
* [ ] support multiple UI design systems


## FAQ
### 1. Why named fryweb
At first, fryweb is named fryhcs, means **FRY** **H**tml, **C**ss and Java**S**cript, in pure Python,
no nodejs-based tooling needed!

But this name is too difficult to remember, so it's named fryweb.

By coincidence, this project is created by the father of one boy(**F**ang**R**ui) and one girl(**F**ang**Y**i)

### 2. Why is the file format named to be .fry
Originally, the file format is named .pyx, just similar to famous React jsx. But .pyx is already
used in Cython, so it has to be renamed.

First it's renamed to be .fy, easy to write. Unfortunately, .fy is also used by a rubyvm-based
language called fancy. But from [rubygems][1] and [github][2], there's no activity for ten years
on this project, and the last version is 0.10.0.

At last, it's named to be .fry.

[1]: https://rubygems.org/gems/fancy
[2]: https://github.com/bakkdoor/fancy

### 3. Is it good to merge frontend code and backend code into one file?
Good question. We always say frontend-backend separation. But logically, for a web app, the
frontend code is usually tightly coupled with the backend code, although they are running on
different platform, at different place. When we change one function, usually we should change
backend logic and frontend logic together, from diffent files.

web app code should be separated by logic, not by the deployment and running place. We can use
building tools to separate code for different place.

### 4. Why not use the major tooling based on nodejs to handle frontend code?
Ok, there's too many tools! For me, as a backend developer, I always feel the frontend tools are
too complex, gulp, grunt, browsify, webpack, vite, postcss, tailwind, esbuild, rollup..., with too
many configuration files.

Yes, npm registy is a great frontend ecosystem, pypi is a great backend ecosystem. I need them,
but I only need the great libraries in these two great ecosystems, not soooo many different tools.
so one command `fry` is enough, it can be used to generate html, css, javascript, and handle
the downloading of javascript libraries from npm registry (soon).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "fryweb",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "asgi, fry, fryweb, server, wsgi",
    "author": null,
    "author_email": "Zenk Ju <juzejian@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/8e/a2/6b703b67b98744a3adb5d006e045a484ea35ed1f34198d0c23796e8d0be0/fryweb-0.3.4.tar.gz",
    "platform": null,
    "description": "# Fryweb\nA Python library to generate HTML, Javascript and CSS, based on .fry file.\n\nFry is jsx in python, it's the core of this project.\n\nFryweb is heavily inspired by React JSX, TailwindCSS, WindiCSS in JS ecosystem.\n\n## Features\n* Support fry extension to normal python file, similar to jsx, write html tags in python file.\n* Provide a fry loader for python import machanism, load and execute .fry files directly by CPython.\n* Provide a utility-first css framework, similar to TailwindCSS, support attributify mode similar to WindiCSS.\n* Support wsgi/asgi application.\n* Provide pygments lexer for fry.\n* Provide a development server which supports server/browser auto reloading when file saved.\n* Provide a command line tool `fry`, build css/js, highlight and run fry file and run development server. \n* Support plugin machanism, anyone can extends with her/his own custom css utilities.\n\nAll features are implemented in pure Python, no node.js ecosystem is required.\n\n## Installation\n\n```bash\n$ pip install fryweb\n```\n\n## Usage\n\n### 1. Basic\ncreate app.fry file:\n\n```python\nfrom fryweb import html, Element\nfrom flask import Flask\n\napp = Flask(__name__)\n\ndef App():\n    <template>\n      <h1 text-cyan-500 hover:text-cyan-600 text-center mt-100px>\n        Hello FryWEB!\n      </h1>\n    </template>\n\n@app.get('/')\ndef index():\n    return html(App, \"Hello\")\n```\n\nin the same directory as app.fry, run command:\n\n```bash\n$ fry topy app.fry\n```\n\ncheck the generated python content:\n\n```python\nfrom fryweb import html, Element\nfrom flask import Flask\n\napp = Flask(__name__)\n\ndef App():\n    return Element(\"h1\", {\"class\": \"text-cyan-500 hover:text-cyan-600 text-center mt-100px\", \"children\": [\"Hello FryWEB!\"]})\n\n@app.get('/')\ndef index():\n    return html(App, \"Hello\")\n\n```\n\nTo generate CSS file `static/css/styles.css`, run command:\n```bash\n$ fry tocss app.fry\n```\n\nGenerated CSS:\n\n```css\n....\n\n.text-cyan-500 {\n  color: rgb(6 182 212);\n}\n\n.text-center {\n  text-align: center;\n}\n\n.mt-100px {\n  margin-top: 100px;\n}\n\n.hover\\:text-cyan-600:hover {\n  color: rgb(8 145 178);\n}\n\n```\n\nTo serve this app, run command:\n\n```bash\n$ fry dev \n```\n\nOpen browser, access `http://127.0.0.1:5000` to browse the page.\n\nChange the app.fry file, save, check the browser auto reloading.\n\n`fryweb.render` can be used to render component directly.\n\nCreate components.fry and input following code:\n\n```python\nfrom fryweb import Element\n\ndef Component(**props):\n    <template>\n      <h1 text-cyan-500 hover:text-cyan-600 text-center mt-100px>\n        Hello Fryweb!\n      </h1>\n    </template>\n\nif __name__ == '__main__':\n    from fryweb import render\n    print(render(Component))\n```\n\nRun command to see the generated html fragment:\n```bash\n$ fry run component.fry\n```\n\n\n### 2. Using python variable in html markup:\n\n```python\nfrom fryweb import html, Element\nfrom flask import Flask\n\napp = Flask(__name__)\n\ndef App():\n    initial_count = 10\n\n    <template>\n      <div>\n        <h1 text-cyan-500 hover:text-cyan-600 text-center mt-100px>\n          Hello Fryweb!\n        </h1>\n        <p text-indigo-600 text-center mt-9>Count: {initial_count}</p>\n      </div>\n    </template>\n\n@app.get('/')\ndef index():\n    return html(App, \"Hello\")\n```\n\nGenerated python:\n\n```python\nfrom fryweb import html, Element\nfrom flask import Flask\n\napp = Flask(__name__)\n\ndef App():\n    initial_count = 10\n    return Element(\"div\", {\"children\": [Element(\"h1\", {\"class\": \"text-cyan-500 hover:text-cyan-600 text-center mt-100px\", \"children\": [\"Hello Fryweb!\"]}), Element(\"p\", {\"class\": \"text-indigo-600 text-center mt-9\", \"children\": [\"Count:\", (initial_count)]})]})\n\n@app.get('/')\ndef index():\n    return html(App, \"Hello\")\n\n```\n\n### 3. Add js logic and reactive variable(signal/computed):\n\n```python\nfrom fryweb import html, Element\nfrom flask import Flask\n\napp = Flask(__name__)\n\ndef App():\n    initial_count = 20\n\n    <template>\n       <div>\n         <h1 ref=(header) text-cyan-500 hover:text-cyan-600 text-center mt-100px>\n           Hello Fryweb!\n         </h1>\n         <p text-indigo-600 text-center mt-9>\n           Count:\n           <span text-red-600>[{initial_count}](count)</span>\n         </p>\n         <p text-indigo-600 text-center mt-9>\n           Double:\n           <span text-red-600>[{initial_count*2}](doubleCount)</span>\n         </p>\n         <div flex w-full justify-center>\n           <button\n             @click=(increment)\n             class=\"inline-flex items-center justify-center h-10 gap-2 px-5 text-sm font-medium tracking-wide text-white transition duration-300 rounded focus-visible:outline-none whitespace-nowrap bg-emerald-500 hover:bg-emerald-600 focus:bg-emerald-700 disabled:cursor-not-allowed disabled:border-emerald-300 disabled:bg-emerald-300 disabled:shadow-none\">\n             Increment\n           </button>\n         </div>\n       </div>\n    </template>\n\n    <script initial={initial_count}>\n       import {signal, computed} from \"fryweb\"\n \n       let count = signal(initial)\n \n       let doubleCount = computed(()=>count.value*2)\n \n       function increment() {\n           count.value ++;\n           header.textContent = `Hello Fryweb(${count.value})`;\n       }\n    </script>\n\n\n@app.get('/')\ndef index():\n    return html(App, \"Hello\")\n```\n\nGenerated python:\n\n```python\nfrom fryweb import html, Element\nfrom flask import Flask\n\napp = Flask(__name__)\n\ndef App():\n    initial_count = 20\n\n    return Element(\"div\", {\"call-client-script\": [\"App-1171022438ea1f5e3d31f5fb191ca3c18adfda49\", [(\"initial\", (initial_count))]], \"children\": [Element(\"h1\", {\"ref:header\": Element.ClientEmbed(0), \"class\": \"text-cyan-500 hover:text-cyan-600 text-center mt-100px\", \"children\": [\"Hello Fryweb!\"]}), Element(\"p\", {\"class\": \"text-indigo-600 text-center mt-9\", \"children\": [\"Count:\", Element(\"span\", {\"class\": \"text-red-600\", \"children\": [Element(\"span\", {\"*\": Element.ClientEmbed(1), \"children\": [f\"\"\"{initial_count}\"\"\"]})]})]}), Element(\"p\", {\"class\": \"text-indigo-600 text-center mt-9\", \"children\": [\"Double:\", Element(\"span\", {\"class\": \"text-red-600\", \"children\": [Element(\"span\", {\"*\": Element.ClientEmbed(2), \"children\": [f\"\"\"{initial_count*2}\"\"\"]})]})]}), Element(\"div\", {\"class\": \"flex w-full justify-center\", \"children\": [Element(\"button\", {\"@click\": Element.ClientEmbed(3), \"class\": \"inline-flex items-center justify-center h-10 gap-2 px-5 text-sm font-medium tracking-wide text-white transition duration-300 rounded focus-visible:outline-none whitespace-nowrap bg-emerald-500 hover:bg-emerald-600 focus:bg-emerald-700 disabled:cursor-not-allowed disabled:border-emerald-300 disabled:bg-emerald-300 disabled:shadow-none\", \"children\": [\"Increment\"]})]})]})\n\n\n@app.get('/')\ndef index():\n    return html(App, \"Hello\")\n```\n\nGenerated js script `static/js/components/.tmp/App-1171022438ea1f5e3d31f5fb191ca3c18adfda49.js`:\n\n```js\nexport { hydrate as hydrateAll } from \"fryweb\";\nexport const hydrate = async function (element$$, doHydrate$$) {\n    const { header, initial } = element$$.fryargs;\n\n              const {signal, computed} = await import(\"fryweb\")\n\n              let count = signal(initial)\n\n              let doubleCount = computed(()=>count.value*2)\n\n              function increment() {\n                  count.value ++;\n                  header.textContent = `Hello Fryweb(${count.value})`;\n              }\n\n    const embeds$$ = [header, count, doubleCount, increment];\n    doHydrate$$(element$$, embeds$$);\n};\n```\n\nGenerated HTML:\n\n```html\n<!DOCTYPE html>\n<html lang=en>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Hello</title>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <link rel=\"stylesheet\" href=\"/static/css/styles.css\">\n  </head>\n  <body>\n    <div><script data-fryid=\"1\" data-fryclass=\"app:App\" data-initial=\"20\"></script><h1 class=\"text-cyan-500 hover:text-cyan-600 text-center mt-100px\" data-fryembed=\"1/0-ref-header\">Hello Fryweb!</h1><p class=\"text-indigo-600 text-center mt-9\">Count:<span class=\"text-red-600\"><span data-fryembed=\"1/1-text\">20</span></span></p><p class=\"text-indigo-600 text-center mt-9\">Double:<span class=\"text-red-600\"><span data-fryembed=\"1/2-text\">40</span></span></p><div class=\"flex w-full justify-center\"><button class=\"inline-flex items-center justify-center h-10 gap-2 px-5 text-sm font-medium tracking-wide text-white transition duration-300 rounded focus-visible:outline-none whitespace-nowrap bg-emerald-500 hover:bg-emerald-600 focus:bg-emerald-700 disabled:cursor-not-allowed disabled:border-emerald-300 disabled:bg-emerald-300 disabled:shadow-none\" data-fryembed=\"1/3-event-click\">Increment</button></div></div>\n\n    <script type=\"module\">\n      let hydrates = {};\n      import { hydrate as hydrate_0, hydrateAll } from '/static/js/components/1171022438ea1f5e3d31f5fb191ca3c18adfda49.js';\n      hydrates['1'] = hydrate_0;\n      await hydrateAll(hydrates);\n    </script>\n\n  <script type=\"module\">\n    let serverId = null;\n    let eventSource = null;\n    let timeoutId = null;\n    function checkAutoReload() {\n        if (timeoutId !== null) clearTimeout(timeoutId);\n        timeoutId = setTimeout(checkAutoReload, 1000);\n        if (eventSource !== null) eventSource.close();\n        eventSource = new EventSource(\"/_check_hotreload\");\n        eventSource.addEventListener('open', () => {\n            console.log(new Date(), \"Auto reload connected.\");\n            if (timeoutId !== null) clearTimeout(timeoutId);\n            timeoutId = setTimeout(checkAutoReload, 1000);\n        });\n        eventSource.addEventListener('message', (event) => {\n            const data = JSON.parse(event.data);\n            if (serverId === null) {\n                serverId = data.serverId;\n            } else if (serverId !== data.serverId) {\n                if (eventSource !== null) eventSource.close();\n                if (timeoutId !== null) clearTimeout(timeoutId);\n                location.reload();\n                return;\n            }\n            if (timeoutId !== null) clearTimeout(timeoutId);\n            timeoutId = setTimeout(checkAutoReload, 1000);\n        });\n    }\n    checkAutoReload();\n  </script>\n\n  </body>\n</html>\n```\n\n### 4. Reference html element and component element in js logic:\n\n```python\nfrom fryweb import Element, html\nfrom flask import Flask\n\napp = Flask(__name__)\n\n@app.get('/')\ndef index():\n    return html(RefApp, title=\"test ref\")\n\ndef Refed():\n    <template>\n      <div>\n        hello world\n      </div>\n    </template>\n    <script>\n      export default {\n          hello() {\n              console.log('hello hello')\n          }\n      }\n    </script>\n\ndef RefApp():\n\n    <template>\n      <div w-full h-100vh flex flex-col gap-y-10 justify-center items-center>\n        <p ref=(foo) text-indigo-600 text-6xl transition-transform duration-1500>\n          Hello World!\n        </p>\n        <p ref=(bar) text-cyan-600 text-6xl transition-transform duration-1500>\n          Hello Fryweb!\n        </p>\n        {<p refall=(foobar)>foobar</p> for i in range(3)}\n        <Refed ref=(refed) refall=(refeds)/>\n        {<Refed refall=(refeds) /> for i in range(2)}\n      </div>\n    </template>\n\n    <script foo bar foobar refed refeds>\n      setTimeout(()=>{\n        foo.style.transform = \"skewY(180deg)\";\n      }, 1000);\n      setTimeout(()=>{\n        bar.style.transform = \"skewY(180deg)\";\n      }, 2500);\n      for (const fb of foobar) {\n        console.log(fb);\n      }\n      refed.hello()\n      for (const r of refeds) {\n          r.hello()\n      }\n    </script>\n\nif __name__ == '__main__':\n    print(html(RefApp))\n```\n\n## Command Line Tool `fry`\n\n## Configuration\n\n## Django Integration\n\n## Flask Integration\n\n## FastAPI Integration\n\n## License\nMIT License\n\n## Road Map\n* [ ] support component fetch from backend\n* [ ] support component CRUD from frontend\n* [ ] support multiple UI design systems\n\n\n## FAQ\n### 1. Why named fryweb\nAt first, fryweb is named fryhcs, means **FRY** **H**tml, **C**ss and Java**S**cript, in pure Python,\nno nodejs-based tooling needed!\n\nBut this name is too difficult to remember, so it's named fryweb.\n\nBy coincidence, this project is created by the father of one boy(**F**ang**R**ui) and one girl(**F**ang**Y**i)\n\n### 2. Why is the file format named to be .fry\nOriginally, the file format is named .pyx, just similar to famous React jsx. But .pyx is already\nused in Cython, so it has to be renamed.\n\nFirst it's renamed to be .fy, easy to write. Unfortunately, .fy is also used by a rubyvm-based\nlanguage called fancy. But from [rubygems][1] and [github][2], there's no activity for ten years\non this project, and the last version is 0.10.0.\n\nAt last, it's named to be .fry.\n\n[1]: https://rubygems.org/gems/fancy\n[2]: https://github.com/bakkdoor/fancy\n\n### 3. Is it good to merge frontend code and backend code into one file?\nGood question. We always say frontend-backend separation. But logically, for a web app, the\nfrontend code is usually tightly coupled with the backend code, although they are running on\ndifferent platform, at different place. When we change one function, usually we should change\nbackend logic and frontend logic together, from diffent files.\n\nweb app code should be separated by logic, not by the deployment and running place. We can use\nbuilding tools to separate code for different place.\n\n### 4. Why not use the major tooling based on nodejs to handle frontend code?\nOk, there's too many tools! For me, as a backend developer, I always feel the frontend tools are\ntoo complex, gulp, grunt, browsify, webpack, vite, postcss, tailwind, esbuild, rollup..., with too\nmany configuration files.\n\nYes, npm registy is a great frontend ecosystem, pypi is a great backend ecosystem. I need them,\nbut I only need the great libraries in these two great ecosystems, not soooo many different tools.\nso one command `fry` is enough, it can be used to generate html, css, javascript, and handle\nthe downloading of javascript libraries from npm registry (soon).\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "A asgi/wsgi server that can generate HTML, Javascript and CSS, based on fry file",
    "version": "0.3.4",
    "project_urls": {
        "Bug Tracker": "https://github.com/frybox/fryweb/issues",
        "Homepage": "https://github.com/frybox/fryweb",
        "Source": "https://github.com/frybox/fryweb"
    },
    "split_keywords": [
        "asgi",
        " fry",
        " fryweb",
        " server",
        " wsgi"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "583031c960e881baa96499fd52122235ec3f94977f714b74c8e5aed1474127f1",
                "md5": "a5acebe7399290ff291566507ff66960",
                "sha256": "569be2f634ab77de4ff05b847761d976c5ed47bb16943a724777f6515103ec95"
            },
            "downloads": -1,
            "filename": "fryweb-0.3.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a5acebe7399290ff291566507ff66960",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 99442,
            "upload_time": "2024-12-14T03:41:24",
            "upload_time_iso_8601": "2024-12-14T03:41:24.337486Z",
            "url": "https://files.pythonhosted.org/packages/58/30/31c960e881baa96499fd52122235ec3f94977f714b74c8e5aed1474127f1/fryweb-0.3.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8ea26b703b67b98744a3adb5d006e045a484ea35ed1f34198d0c23796e8d0be0",
                "md5": "40ff1201aed13c8cb225b9dc6b960b27",
                "sha256": "377a21542a2c42ce68d33d7d303f4b6b30f070f0f632354f43ec6c859fa8ed1b"
            },
            "downloads": -1,
            "filename": "fryweb-0.3.4.tar.gz",
            "has_sig": false,
            "md5_digest": "40ff1201aed13c8cb225b9dc6b960b27",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 120591,
            "upload_time": "2024-12-14T03:41:27",
            "upload_time_iso_8601": "2024-12-14T03:41:27.674731Z",
            "url": "https://files.pythonhosted.org/packages/8e/a2/6b703b67b98744a3adb5d006e045a484ea35ed1f34198d0c23796e8d0be0/fryweb-0.3.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-12-14 03:41:27",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "frybox",
    "github_project": "fryweb",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "fryweb"
}
        
Elapsed time: 0.41642s