Skip to content
Merged

Next #219

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
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
<img src='https://raw.githubusercontent.com/nullstack/nullstack/master/nullstack.png' height='60' alt='Nullstack'>

Full-stack javascript components for one-dev armies.
Feature-Driven Fullstack JavaScript Components

## What is Nullstack?

Nullstack is a full-stack framework for building progressive web applications.
Write the back-end and the front-end of a feature in a single component and let the framework decide where the code should run.

It connects a stateful UI layer to specialized microservices in the same component using vanilla javascript.

Focus on solving your business logic instead of writing glue code.
Nullstack provides you all the tools you need to stay focused on the product.

Learn more about [Nullstack](https://nullstack.app)

Expand Down
10 changes: 10 additions & 0 deletions loaders/ignore-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';
/*
Copyright (c) 2016 Cherry Ng
MIT Licensed
https://npmjs.com/package/ignore-loader
*/
module.exports = function() {
this.cacheable && this.cacheable();
return '';
}
20 changes: 20 additions & 0 deletions loaders/string-replace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
Copyright (c) 2015 Valentyn Barmashyn
MIT Licensed
Original: https://npmjs.com/package/string-replace-loader
*/
module.exports = function(source, map) {
this.cacheable();

const optionsArray = this.getOptions().multiple;
let newSource = source;

for (const options of optionsArray) {
newSource = newSource.replace(
new RegExp(options.search, options.search.flags || options.flags || ''),
options.replace
);
}

this.callback(null, newSource, map);
}
File renamed without changes.
25 changes: 5 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nullstack",
"version": "0.15.3",
"version": "0.15.5",
"description": "Full-stack Javascript Components for one-dev armies",
"main": "nullstack.js",
"author": "Mortaro",
Expand All @@ -12,39 +12,24 @@
},
"types": "./types/index.d.ts",
"dependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.2.2",
"@babel/node": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-proposal-decorators": "^7.2.3",
"@babel/plugin-proposal-export-default-from": "^7.2.0",
"@babel/plugin-transform-react-jsx": "^7.8.3",
"@babel/plugin-transform-typescript": "^7.16.1",
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.5",
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"@swc/core": "^1.2.171",
"@babel/parser": "^7.2.2",
"@babel/traverse": "^7.2.2",
"body-parser": "^1.19.0",
"commander": "^8.3.0",
"cors": "^2.8.5",
"css-loader": "^6.6.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"fs-extra": "^10.0.0",
"ignore-loader": "^0.1.2",
"mini-css-extract-plugin": "^2.4.5",
"node-fetch": "2.6.7",
"nodemon-webpack-plugin": "^4.3.1",
"raw-loader": "^4.0.2",
"sass": "^1.32.11",
"sass-loader": "^8.0.2",
"string-replace-loader": "^3.1.0",
"swc-loader": "^0.2.0",
"terser-webpack-plugin": "^5.3.0",
"ts-loader": "^9.2.6",
"typescript": "^4.5.3",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1",
"webpack-livereload-plugin": "^2.3.0",
"ws": "^7.4.4"
}
}
22 changes: 11 additions & 11 deletions server/worker.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import activate from '!!raw-loader!../workers/activate.js';
import cacheFirst from '!!raw-loader!../workers/cacheFirst.js';
import dynamicFetch from '!!raw-loader!../workers/dynamicFetch.js';
import dynamicInstall from '!!raw-loader!../workers/dynamicInstall.js';
import load from '!!raw-loader!../workers/load.js';
import networkDataFirst from '!!raw-loader!../workers/networkDataFirst.js';
import networkFirst from '!!raw-loader!../workers/networkFirst.js';
import staleWhileRevalidate from '!!raw-loader!../workers/staleWhileRevalidate.js';
import staticFetch from '!!raw-loader!../workers/staticFetch.js';
import staticHelpers from '!!raw-loader!../workers/staticHelpers.js';
import staticInstall from '!!raw-loader!../workers/staticInstall.js';
import activate from '../workers/activate.js?raw';
import cacheFirst from '../workers/cacheFirst.js?raw';
import dynamicFetch from '../workers/dynamicFetch.js?raw';
import dynamicInstall from '../workers/dynamicInstall.js?raw';
import load from '../workers/load.js?raw';
import networkDataFirst from '../workers/networkDataFirst.js?raw';
import networkFirst from '../workers/networkFirst.js?raw';
import staleWhileRevalidate from '../workers/staleWhileRevalidate.js?raw';
import staticFetch from '../workers/staticFetch.js?raw';
import staticHelpers from '../workers/staticHelpers.js?raw';
import staticInstall from '../workers/staticInstall.js?raw';
import { existsSync, readdirSync, readFileSync } from 'fs';
import path from 'path';
import environment from './environment';
Expand Down
2 changes: 1 addition & 1 deletion shared/generateTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async function generateBranch(parent, node, depth, scope) {
}

if (isClass(node)) {
const key = node.attributes.key ? node.attributes.key : generateKey(node, depth) + (node.attributes.route ? scope.context.router.url : '')
const key = node.attributes.key ? node.attributes.key : generateKey(node, depth) + (node.attributes.route ? (scope.context.environment.mode === 'ssg' ? scope.context.router.path : scope.context.router.url) : '')
if (
scope.context.environment.client &&
scope.context.router._changed &&
Expand Down
1 change: 0 additions & 1 deletion tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"nullstack": "*",
"puppeteer": "^5.5.0",
"jest-puppeteer": "^6.0.3",
"webpack-cli": "^3.3.12",
"glob": "^7.1.7",
"purgecss-webpack-plugin": "^4.1.3"
},
Expand Down
10 changes: 9 additions & 1 deletion tests/src/LazyComponent.njs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ import Nullstack from 'nullstack';

class LazyComponent extends Nullstack {

static async serverFunctionWorks() {
return true
}

async initiate() {
this.safelisted = await this.serverFunctionWorks()
}

render() {
return (
<div data-lazy> LazyComponent </div>
<div data-lazy data-safelisted={this.safelisted}> LazyComponent </div>
)
}

Expand Down
6 changes: 6 additions & 0 deletions tests/src/LazyComponent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ describe('LazyComponent', () => {
expect(element).toBeTruthy();
});

test('lazy components need to be safelisted in order to call server functions', async () => {
await page.waitForSelector('[data-safelisted]');
const element = await page.$('[data-safelisted]');
expect(element).toBeTruthy();
});

});
5 changes: 5 additions & 0 deletions tests/src/LazyComponentLoader.njs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import Nullstack from 'nullstack';
import LazyComponentSafelist from './LazyComponent'

let LazyComponent

class LazyComponentLoader extends Nullstack {

static async start() {
LazyComponentSafelist.safelist()
}

async hydrate() {
LazyComponent = (await import('./LazyComponent')).default
}
Expand Down
94 changes: 50 additions & 44 deletions types/ClientContext.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,99 +8,105 @@ import { NullstackSettings } from "./Settings";
import { NullstackWorker } from "./Worker";

/**
* https://nullstack.app/context
* @see https://nullstack.app/context
*/
export type NullstackClientContext = {

/**
* Information about the document `head` metatags.
*
* https://nullstack.app/context-page
*
* @see https://nullstack.app/context-page
*/
page?: NullstackPage,
page?: NullstackPage;

/**
* Information about the app manifest and some metatags.
*
* https://nullstack.app/context-project
* @see https://nullstack.app/context-project
*/
project?: NullstackProject,
project?: NullstackProject;

/**
* Gives you granular control of your PWA behavior.
*
* https://nullstack.app/service-worker
*
* @see https://nullstack.app/service-worker
*/
worker?: NullstackWorker,
worker?: NullstackWorker;

/**
* It gives you information about the instance lifecycle and it's unique [key](https://nullstack.app/instance-self#instance-key).
*
* https://nullstack.app/instance-self
* It gives you information about the instance lifecycle and it's unique key.
*
* @see https://nullstack.app/instance-self
* @see https://nullstack.app/instance-self#instance-key
*/
self?: NullstackSelf,
self?: NullstackSelf;

/**
* It gives you information about the element dataset.
*
* Any `data-*` attributes will receive a respective camelized key on this object.
*
* Only on client.
*
* https://nullstack.app/context-data
*
* @scope client
* @see https://nullstack.app/context-data
*/
data?: object,
data?: Record<string, string>;

/**
* It gives you all active instances of the application.
*
* Adding a [key](https://nullstack.app/instance-self#instance-key) to a Component adds it here.
*
* Only on client.
*
* https://nullstack.app/context-instances
*
* @scope client
* @see https://nullstack.app/context-instances
*/
instances?: object,
instances?: Record<string, any>;

/**
* It gives you information about the current environment.
*
* https://nullstack.app/context-environment
*
* @see https://nullstack.app/context-environment
*/
environment?: NullstackEnvironment,
environment?: NullstackEnvironment;

/**
* Each query string param is mapped to this object.
*
* https://nullstack.app/routes-and-params#params
*
* @see https://nullstack.app/routes-and-params#params
* @example
* "/?expanded=true&page=2" === {expanded: true, page: 2}
* ```
* "/?expanded=true&page=2" ===
* {expanded: true, page: 2}
* ```
*/
params?: NullstackParams,
params?: NullstackParams;

/**
* Nullstack router.
*
* https://nullstack.app/routes-and-params#router
*
* @see https://nullstack.app/routes-and-params#router
*/
router?: NullstackRouter,
router?: NullstackRouter;

/**
* You can assign any key with any type of public information.
*
* .env `NULLSTACK_SETTINGS_PUBLIC_KEY` -> `settings.publicKey`
*
* https://nullstack.app/context-settings
*
* @example
* ```
* // .env NULLSTACK_SETTINGS_PUBLIC_KEY
* settings.publicKey
* ```
* @see https://nullstack.app/context-settings
*/
settings?: NullstackSettings,
settings?: NullstackSettings;

/**
* Children elements of this component.
*
* https://nullstack.app/renderable-components#components-with-children
*
* @see https://nullstack.app/renderable-components#components-with-children
*/
children?: any,
children?: any;

[key: string]: any
/**
* Custom context prop.
*/
[key: string]: any;

};
16 changes: 8 additions & 8 deletions types/Environment.d.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
export type NullstackEnvironment = {

client: boolean,
client: boolean;

server: boolean,
server: boolean;

development: boolean,
development: boolean;

production: boolean,
production: boolean;

static: boolean,
static: boolean;

/**
* md5 hash of the current environment folder outputs.
*
* https://nullstack.app/context-environment
*
* @see https://nullstack.app/context-environment
*/
key: string
key: string;

};
Loading