@lolyjs/core 0.2.0-alpha.21 → 0.2.0-alpha.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -1
- package/dist/cli.cjs +97 -42
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +97 -42
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +107 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +107 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -392,6 +392,64 @@ export default function Dashboard() {
|
|
|
392
392
|
}
|
|
393
393
|
```
|
|
394
394
|
|
|
395
|
+
### 📄 Static Files & Assets
|
|
396
|
+
|
|
397
|
+
Loly serves static files from the `public/` directory at the root of your application. This is perfect for SEO files like `sitemap.xml`, `robots.txt`, favicons, and other static assets.
|
|
398
|
+
|
|
399
|
+
**How it works:**
|
|
400
|
+
- Files in `public/` are served at the root URL (e.g., `public/sitemap.xml` → `/sitemap.xml`)
|
|
401
|
+
- Static files have **priority over dynamic routes** - if a file exists in `public/`, it will be served instead of matching a route
|
|
402
|
+
- Perfect for SEO: Google automatically finds `sitemap.xml` and `robots.txt` at the root
|
|
403
|
+
- Works in both development and production environments
|
|
404
|
+
- Subdirectories are supported: `public/assets/logo.png` → `/assets/logo.png`
|
|
405
|
+
|
|
406
|
+
**Directory Structure:**
|
|
407
|
+
```
|
|
408
|
+
public/
|
|
409
|
+
├── sitemap.xml # Available at /sitemap.xml
|
|
410
|
+
├── robots.txt # Available at /robots.txt
|
|
411
|
+
├── favicon.ico # Available at /favicon.ico
|
|
412
|
+
└── assets/
|
|
413
|
+
├── logo.png # Available at /assets/logo.png
|
|
414
|
+
└── images/ # Available at /assets/images/*
|
|
415
|
+
└── hero.jpg
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
**SEO Example:**
|
|
419
|
+
|
|
420
|
+
Create `public/sitemap.xml`:
|
|
421
|
+
```xml
|
|
422
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
423
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
424
|
+
<url>
|
|
425
|
+
<loc>https://example.com/</loc>
|
|
426
|
+
<lastmod>2024-01-01</lastmod>
|
|
427
|
+
<changefreq>daily</changefreq>
|
|
428
|
+
<priority>1.0</priority>
|
|
429
|
+
</url>
|
|
430
|
+
</urlset>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Create `public/robots.txt`:
|
|
434
|
+
```
|
|
435
|
+
User-agent: *
|
|
436
|
+
Allow: /
|
|
437
|
+
|
|
438
|
+
Sitemap: https://example.com/sitemap.xml
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Both files will be automatically available at `/sitemap.xml` and `/robots.txt` respectively, and search engines will find them at the standard locations.
|
|
442
|
+
|
|
443
|
+
**Configuration:**
|
|
444
|
+
The static directory can be customized in `loly.config.ts`:
|
|
445
|
+
```tsx
|
|
446
|
+
export default {
|
|
447
|
+
directories: {
|
|
448
|
+
static: "public", // Default: "public"
|
|
449
|
+
},
|
|
450
|
+
} satisfies FrameworkConfig;
|
|
451
|
+
```
|
|
452
|
+
|
|
395
453
|
### 🔌 API Routes
|
|
396
454
|
|
|
397
455
|
Create RESTful APIs with flexible middleware support:
|
|
@@ -667,7 +725,7 @@ your-app/
|
|
|
667
725
|
│ └── events.ts # WebSocket namespace /chat
|
|
668
726
|
├── components/ # React components
|
|
669
727
|
├── lib/ # Utilities
|
|
670
|
-
├── public/ # Static files
|
|
728
|
+
├── public/ # Static files (served at root: /sitemap.xml, /robots.txt, etc.)
|
|
671
729
|
├── loly.config.ts # Framework configuration
|
|
672
730
|
├── init.server.ts # Server initialization (DB, services, etc.)
|
|
673
731
|
└── package.json
|
package/dist/cli.cjs
CHANGED
|
@@ -13754,51 +13754,88 @@ function createPathAliasPlugin(projectRoot, outDir) {
|
|
|
13754
13754
|
} catch {
|
|
13755
13755
|
}
|
|
13756
13756
|
}
|
|
13757
|
+
function resolveAliasToRelative(importPath, sourceFile) {
|
|
13758
|
+
if (importPath.startsWith(".") || importPath.startsWith("/") || import_path25.default.isAbsolute(importPath) || importPath.includes("node_modules")) {
|
|
13759
|
+
return null;
|
|
13760
|
+
}
|
|
13761
|
+
for (const [aliasKey, aliasPath] of Object.entries(aliases)) {
|
|
13762
|
+
if (importPath.startsWith(aliasKey + "/") || importPath === aliasKey) {
|
|
13763
|
+
const restPath = importPath.startsWith(aliasKey + "/") ? importPath.slice(aliasKey.length + 1) : "";
|
|
13764
|
+
const resolvedPath = restPath ? import_path25.default.join(aliasPath, restPath) : aliasPath;
|
|
13765
|
+
let actualPath = null;
|
|
13766
|
+
const extensions = [".ts", ".tsx", ".js", ".jsx", ".json"];
|
|
13767
|
+
if (import_fs16.default.existsSync(resolvedPath) && import_fs16.default.statSync(resolvedPath).isDirectory()) {
|
|
13768
|
+
for (const ext of extensions) {
|
|
13769
|
+
const indexPath = import_path25.default.join(resolvedPath, `index${ext}`);
|
|
13770
|
+
if (import_fs16.default.existsSync(indexPath)) {
|
|
13771
|
+
actualPath = indexPath;
|
|
13772
|
+
break;
|
|
13773
|
+
}
|
|
13774
|
+
}
|
|
13775
|
+
} else {
|
|
13776
|
+
for (const ext of extensions) {
|
|
13777
|
+
const filePath = resolvedPath + ext;
|
|
13778
|
+
if (import_fs16.default.existsSync(filePath)) {
|
|
13779
|
+
actualPath = filePath;
|
|
13780
|
+
break;
|
|
13781
|
+
}
|
|
13782
|
+
}
|
|
13783
|
+
if (!actualPath && import_fs16.default.existsSync(resolvedPath)) {
|
|
13784
|
+
actualPath = resolvedPath;
|
|
13785
|
+
}
|
|
13786
|
+
}
|
|
13787
|
+
if (actualPath) {
|
|
13788
|
+
const relativePath = import_path25.default.relative(outDir, actualPath);
|
|
13789
|
+
const normalizedPath = relativePath.replace(/\\/g, "/");
|
|
13790
|
+
const finalPath = normalizedPath.startsWith(".") ? normalizedPath : `./${normalizedPath}`;
|
|
13791
|
+
const ext = import_path25.default.extname(finalPath);
|
|
13792
|
+
const pathWithoutExt = ext === ".json" ? finalPath : finalPath.slice(0, -ext.length);
|
|
13793
|
+
return pathWithoutExt;
|
|
13794
|
+
}
|
|
13795
|
+
}
|
|
13796
|
+
}
|
|
13797
|
+
return null;
|
|
13798
|
+
}
|
|
13757
13799
|
return {
|
|
13758
13800
|
name: "path-alias-resolver",
|
|
13759
13801
|
setup(build) {
|
|
13760
|
-
build.
|
|
13761
|
-
|
|
13802
|
+
build.onLoad({ filter: /\.(ts|tsx|js|jsx)$/ }, (args) => {
|
|
13803
|
+
const fileName = import_path25.default.basename(args.path);
|
|
13804
|
+
const isServerFile = SERVER_FILES.some((f) => fileName === `${f}.ts` || fileName === `${f}.tsx` || fileName === `${f}.js` || fileName === `${f}.jsx`);
|
|
13805
|
+
const isInProjectRoot = import_path25.default.dirname(args.path) === projectRoot;
|
|
13806
|
+
if (!isServerFile || !isInProjectRoot) {
|
|
13762
13807
|
return null;
|
|
13763
13808
|
}
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
}
|
|
13778
|
-
} else {
|
|
13779
|
-
for (const ext of extensions) {
|
|
13780
|
-
const filePath = resolvedPath + ext;
|
|
13781
|
-
if (import_fs16.default.existsSync(filePath)) {
|
|
13782
|
-
actualPath = filePath;
|
|
13783
|
-
break;
|
|
13784
|
-
}
|
|
13785
|
-
}
|
|
13786
|
-
if (!actualPath && import_fs16.default.existsSync(resolvedPath)) {
|
|
13787
|
-
actualPath = resolvedPath;
|
|
13788
|
-
}
|
|
13789
|
-
}
|
|
13790
|
-
if (actualPath) {
|
|
13791
|
-
const relativePath = import_path25.default.relative(outDir, actualPath);
|
|
13792
|
-
const normalizedPath = relativePath.replace(/\\/g, "/");
|
|
13793
|
-
const finalPath = normalizedPath.startsWith(".") ? normalizedPath : `./${normalizedPath}`;
|
|
13794
|
-
const ext = import_path25.default.extname(finalPath);
|
|
13795
|
-
const pathWithoutExt = ext === ".json" ? finalPath : finalPath.slice(0, -ext.length);
|
|
13796
|
-
return {
|
|
13797
|
-
path: pathWithoutExt,
|
|
13798
|
-
namespace: "file"
|
|
13799
|
-
};
|
|
13809
|
+
const contents = import_fs16.default.readFileSync(args.path, "utf-8");
|
|
13810
|
+
let transformed = contents;
|
|
13811
|
+
const aliasPatterns = Object.keys(aliases).sort((a, b) => b.length - a.length);
|
|
13812
|
+
for (const aliasKey of aliasPatterns) {
|
|
13813
|
+
const escapedAlias = aliasKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13814
|
+
const aliasInQuotesPattern = new RegExp(
|
|
13815
|
+
`(['"\`])${escapedAlias}(/[^'"\`\\s]*)?(['"\`])`,
|
|
13816
|
+
"g"
|
|
13817
|
+
);
|
|
13818
|
+
transformed = transformed.replace(aliasInQuotesPattern, (match, quote1, rest, quote2) => {
|
|
13819
|
+
const fullPath = aliasKey + (rest || "");
|
|
13820
|
+
const resolved = resolveAliasToRelative(fullPath, args.path);
|
|
13821
|
+
if (resolved) {
|
|
13822
|
+
return `${quote1}${resolved}${quote2}`;
|
|
13800
13823
|
}
|
|
13801
|
-
|
|
13824
|
+
return match;
|
|
13825
|
+
});
|
|
13826
|
+
}
|
|
13827
|
+
return {
|
|
13828
|
+
contents: transformed,
|
|
13829
|
+
loader: import_path25.default.extname(args.path).slice(1)
|
|
13830
|
+
};
|
|
13831
|
+
});
|
|
13832
|
+
build.onResolve({ filter: /.*/ }, (args) => {
|
|
13833
|
+
const resolved = resolveAliasToRelative(args.path, args.importer || "");
|
|
13834
|
+
if (resolved) {
|
|
13835
|
+
return {
|
|
13836
|
+
path: resolved,
|
|
13837
|
+
namespace: "file"
|
|
13838
|
+
};
|
|
13802
13839
|
}
|
|
13803
13840
|
return null;
|
|
13804
13841
|
});
|
|
@@ -14101,6 +14138,9 @@ function getAppDir(projectRoot, config) {
|
|
|
14101
14138
|
function getBuildDir(projectRoot, config) {
|
|
14102
14139
|
return import_path26.default.join(projectRoot, config.directories.build);
|
|
14103
14140
|
}
|
|
14141
|
+
function getStaticDir(projectRoot, config) {
|
|
14142
|
+
return import_path26.default.resolve(projectRoot, config.directories.static);
|
|
14143
|
+
}
|
|
14104
14144
|
|
|
14105
14145
|
// modules/build/index.ts
|
|
14106
14146
|
async function buildApp(options = {}) {
|
|
@@ -14151,12 +14191,13 @@ async function buildApp(options = {}) {
|
|
|
14151
14191
|
}
|
|
14152
14192
|
|
|
14153
14193
|
// src/server.ts
|
|
14154
|
-
var
|
|
14194
|
+
var import_fs21 = __toESM(require("fs"));
|
|
14155
14195
|
var import_path31 = __toESM(require("path"));
|
|
14156
14196
|
|
|
14157
14197
|
// modules/server/setup.ts
|
|
14158
14198
|
var import_express = __toESM(require("express"));
|
|
14159
14199
|
var import_path29 = __toESM(require("path"));
|
|
14200
|
+
var import_fs20 = __toESM(require("fs"));
|
|
14160
14201
|
|
|
14161
14202
|
// ../../node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/index.js
|
|
14162
14203
|
var import_fs19 = require("fs");
|
|
@@ -15998,8 +16039,22 @@ function clearAppRequireCache(appDir) {
|
|
|
15998
16039
|
|
|
15999
16040
|
// modules/server/setup.ts
|
|
16000
16041
|
init_globals();
|
|
16042
|
+
function setupStaticFiles(app, projectRoot, config) {
|
|
16043
|
+
if (!config) return;
|
|
16044
|
+
const staticDir = getStaticDir(projectRoot, config);
|
|
16045
|
+
if (import_fs20.default.existsSync(staticDir)) {
|
|
16046
|
+
app.use(
|
|
16047
|
+
import_express.default.static(staticDir, {
|
|
16048
|
+
// In production, add caching headers for better performance
|
|
16049
|
+
maxAge: process.env.NODE_ENV === "production" ? "1y" : 0,
|
|
16050
|
+
immutable: process.env.NODE_ENV === "production"
|
|
16051
|
+
})
|
|
16052
|
+
);
|
|
16053
|
+
}
|
|
16054
|
+
}
|
|
16001
16055
|
function setupServer(app, options) {
|
|
16002
16056
|
const { projectRoot, appDir, isDev, config } = options;
|
|
16057
|
+
setupStaticFiles(app, projectRoot, config);
|
|
16003
16058
|
const routeLoader = isDev ? new FilesystemRouteLoader(appDir, projectRoot) : new ManifestRouteLoader(projectRoot);
|
|
16004
16059
|
if (isDev) {
|
|
16005
16060
|
let getRoutes2 = function() {
|
|
@@ -17562,7 +17617,7 @@ var setupApplication = async ({
|
|
|
17562
17617
|
// src/server.ts
|
|
17563
17618
|
var import_dotenv2 = __toESM(require("dotenv"));
|
|
17564
17619
|
var envPath = import_path31.default.join(process.cwd(), ".env");
|
|
17565
|
-
if (
|
|
17620
|
+
if (import_fs21.default.existsSync(envPath)) {
|
|
17566
17621
|
import_dotenv2.default.config({ path: envPath });
|
|
17567
17622
|
} else {
|
|
17568
17623
|
import_dotenv2.default.config();
|
|
@@ -17584,7 +17639,7 @@ async function startServer(options = {}) {
|
|
|
17584
17639
|
const port = options.port ?? (process.env.PORT ? parseInt(process.env.PORT, 10) : void 0) ?? config.server.port;
|
|
17585
17640
|
const host = process.env.HOST ?? (!isDev ? "0.0.0.0" : void 0) ?? config.server.host;
|
|
17586
17641
|
const appDir = options.appDir ?? (isDev ? getAppDir(projectRoot, config) : import_path31.default.join(getBuildDir(projectRoot, config), "server"));
|
|
17587
|
-
if (!isDev && !
|
|
17642
|
+
if (!isDev && !import_fs21.default.existsSync(appDir)) {
|
|
17588
17643
|
logger4.error("Compiled directory not found", void 0, {
|
|
17589
17644
|
buildDir: config.directories.build,
|
|
17590
17645
|
appDir,
|