Giter Site home page Giter Site logo

schultek / jaspr Goto Github PK

View Code? Open in Web Editor NEW
949.0 15.0 56.0 6.62 MB

Modern web framework for building websites in Dart. Supports SPAs and SSR.

Home Page: https://jasprpad.schultek.de

License: MIT License

Dart 93.54% HTML 0.58% CSS 0.68% Dockerfile 0.07% JavaScript 0.32% SCSS 4.81%
dart web dart-web hacktoberfest

jaspr's Introduction

Hi, I'm Kilian ๐Ÿ‘‹

I'm passionate about everything Dart & Flutter related ๐Ÿ’™.

Checkout my work here on Github or:


๐ŸŽฏ Featured Project

I'm currently working on a new web framework for Dart - logo jaspr.

It is designed to be SSR-first and has a Flutter-style component system, so that is has next to no learning curve for developers coming from Flutter ๐Ÿ’ช.

It is still in the beta stage and I appreciate any help or feedback.


๐Ÿ“ฏ Recommended Read

Useless Tips for Dart you probably never need.

This 3-part series gives a fun and interesting deep-dive into the abyss of unknown or weird Dart language features. It is a special kind of love letter to Dart.

There exist a lot of great articles highlighting the most useful tips and tricks for Flutter and Dart. They provide great value for both beginners and advanced developers.

This article is not one of them.

This is about the useless tips, the weird quirks, the boring facts and not needed tricks. They are either so absurd you won't ever need them, or so abstruse you don't wish to need them. But they deserve some love, too.


jaspr's People

Contributors

carmas123 avatar cybertheory avatar easazade avatar exoad avatar felipebueno avatar flowhorn avatar fuzzybinary avatar gadfly361 avatar hankg avatar intercolatic avatar juancastillo0 avatar labrom avatar maksimka101 avatar markosullivan94 avatar mjablecnik avatar rexios80 avatar schultek avatar siesdart avatar slightfoot avatar walsha2 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jaspr's Issues

Add example for using docker and cloud run

A dockerized jaspr app that can be deployed to google cloud run.

Can be done together with some other feature or use-case.

This may include:

  • a comprehensive docker file
  • building and deployment instructions (e.g. in README) for cloud run
  • (optionally) integrating with some other gcloud product on the server (e.g. database)

Provide a way to override the base href in `jaspr build`

Hi,

I wonder whether it would be a good idea to implement something within the framework to specify the base href for the generated html. Specially for the build command. This would allow for the developer to not worry about changing the html file between development and production, similar to Flutter's flutter build --web --base-href "/example/". Not sure how that part is implemented, but maybe it could also be used for the package:shelf handler example when one uses a different route. I know it is not that hard to just change the value in CI, however I though I would make an issue to discuss it and know what you think.

Following Flutter, maybe something like this could be inserted in the project generated by jaspr create:

<base href="$JASPR_BASE_HREF"></base>

Or maybe override it if any base tag is found and a --base-href is passed as argument to jaspr build or perhaps a PUBLIC_URL env variable like in react or the base config in vite and next (I have not used it, but the docs say it serves that purpose).

In a new Flutter project the following is generated within the web/index.html:

If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.

The path provided below has to start and end with a slash "/" in order for
it to work correctly.

For more details:

This is a placeholder for base href that will be replaced by the value of
the --base-href argument provided to flutter build.

<base href="$FLUTTER_BASE_HREF">

Thanks!

Add vim keybindings in jasperpad

As the title states, add vim keybindings in jasperpad.

How to enable vim keybindings in dartpad?

  • click on keyboard icon in left bottom corner or press cmd+shift+/
  • toggle use vim keyboard mapping

Webdev remains in detached state when quitting jaspr serve on windows

This is related to dart-lang/sdk#49234

There seems to be no proper way currently to softly terminate child processes. Using process.kill() would forcefully terminate a child process and potentially leave files and sockets broken.

When this is resolved, a solution should be implemented here:

// for (var process in activeProcesses) {
// this would leave open files and ports broken
// we should wait for https://github.com/dart-lang/sdk/issues/49234 to implement a better way
// process.kill();
// }

Theming in jaspr

With the typed Styles class coming in the next release (#18), jaspr will get a powerful mechanism of styling elements through pure type-safe Dart.

There are currently two main cases where Styles can be used:

  • On a single DomComponent rendered as inline styles
  • On a <style> element as a set of css rules

There is a clear mismatch of good vs bad practices when applying these to the declarative component model vs the html/css model:

  1. Styling a component directly

    • From the declarative point of view, applying styles directly to a component is a good practice, since it is explicit and logically tied to the layout. This is also how it is done in Flutter.
    • From the css point of view, extensive use of inline styles is a very bad practice. Styles should be organized in a style sheet (optionally inlined with <style>) and the referred to via ids, classes and other selectors. Inline styles are also less performant than style-sheet rules with e.g. classes (especially for modifying inline-styles vs toggling classes on and off).
  2. Bundling rules in a stylesheet

    • From the css point of view, this is the preferred way and considered a good practice. It is both organized and performant.
    • From the declarative point of view, this is not a good practice, since styling is done separately and not where a component is used. The user also has to manually assign classes (or other selectors) to both the rules and components and keep them in sync.

Additionally there is the issue of bundle size. Right now, Styles would be included in the compiled js bundle, even if they are defined statically and don't change. This is not optimal especially when using ssr where the server could render out the styles to css and the client would only need to know the classes referencing the styles.

Themes

Themes should solve the above problems by

a) providing a familiar, declarative api for the user to define and use themes (which are a collection of styles)
b) internally use css best practices (stylesheets with classes over inline styles)
c) only bundling relevant parts for the client (ideally removing any styles instances)

This could be implemented like this:

  • When defining a theme, the user should define styles for the theme
  • When rendering on the server, the theme should be rendered into css rules referenced by unique (auto-generated?) classes
  • When applying a theme to a component, only the relevant class names should be used

This is currently a very rough idea. I will experiment with this and add more info later on

No executable on pub bin after global activate jaspr on windows

I tried activating jaspr global and it seems it doesn't have any executable on my pub bin.
Already tried to deactivate and activate it again, seems no avail. I also tried to activate other package for example rename without no issue at all.

However running

dart pub global run jaspr --help

seems to work

YK8OFSgcaB.mp4

Router: some missing features - feedback

Hello there!

I'm looking into jaspr for a couple of hobby projects. I ran into an issue with Router. It is mentioned on wiki that it is WIP so I thought I might still be in time to provide some feedback.

  1. First thing I noticed is that you there is no option to specify custom location strategies. Currently only path is considered. Which means I cannot easily "point to" tabs on a specific page. Something like /my-awesome-page?tabbed-view=5-star.
  2. _ResolvedRoute is library private and therefore I cannot easily implement custom Route implementations either, nor can I extend ResolvedRoute since it is effectively sealed with default forwarding factory constructor.

Ultimately, I think my pain point is that I cannot customize routing which is quite restrictive. Even onGenerateRoute receives only the path. I think passing an URI to it would open up quite a few possibilities with minor code change. I could implement my custom routing logic.

Cheers!

Add example for client-side spa with static hosting

A purely client-side jaspr app that can be deployed to any static hosting provider.

Can be done together with some other feature or use-case.

This may include:

  • setup and dev instructions for running a client-side jaspr app
  • building and deployment instructions (e.g. in README) for any general static hosting provider
  • deployment instructions (e.g. in README) for firebase hosting

Overhaul the router package

Currently the router implementation has a number of shortcomings and I want to redo it.

The new implementation should

  • support full uri based routing
  • support nested routing
  • work on both server and client
  • support deferred routes

For the api, it should roughly follow go_router as inspiration.

Problem with update dependencies

@schultek I see you updated version jaspr to 0.2.0 today and I pull changes from main into my local branch but now after run dart pub get I get this error:

Resolving dependencies...
Because jaspr depends on jaspr_test ^0.1.0 which depends on jaspr ^0.1.0, jaspr ^0.1.0 is required.
So, because jaspr is 0.2.0, version solving failed.
Process finished with exit code 1

image

Can you fix it please?

Problem with Safari Browser: FormatException: Scheme not starting with alphabetic character (at character 1)

I started with the quick start guide on https://docs.page/schultek/jaspr/quick-start. I created a project using the basic template, entered the working directory and ran the serve command. I did not open the project in an IDE or edited any files.

When the page is up, I open the browser and visit localhost:8080. I see a white page with ยปHello Worldยซ, but an error gets logged on the server. It seems as if there is an @ symbol in front of the scheme in the generated code.

FormatException: Scheme not starting with alphabetic character (at character 1)

ERROR - 2023-03-31 17:36:08.922166

GET /@http://localhost:8080/app.app.dart.bootstrap.js
Error thrown by handler.
FormatException: Scheme not starting with alphabetic character (at character 1)
@http://localhost:8080/app.app.dart.bootstrap.js
^


dart:core                                           _SimpleUri.resolve
package:shelf_proxy/shelf_proxy.dart 42:28          proxyHandler.<fn>
package:jaspr/src/server/server_handler.dart 51:28  _webdevProxyHandler.<fn>
package:jaspr/src/server/server_handler.dart 34:55  RefreshableHandler.call
package:shelf_gzip/src/gzip_encoding.dart 44:44     createGzipMiddleware.<fn>.<fn>.<fn>
dart:async                                          new Future.sync
package:shelf_gzip/src/gzip_encoding.dart 44:21     createGzipMiddleware.<fn>.<fn>

This is my pubspec.yaml:

name: jaspr_website
description: A basic pure-dart web app with ssr & automatic client hydration.
version: 0.0.1

environment:
  sdk: '>=2.17.0 <3.0.0'

dependencies:
  jaspr: ^0.2.0+4

dev_dependencies:
  build_runner: ^2.2.0
  build_web_compilers: ^3.2.1
  jaspr_builder: ^0.2.0
  lints: ^2.0.1

jaspr --version returns 0.1.0
dart --version returns Dart SDK version: 2.19.6 (stable)

I am using Safari on macOS.

image

UPDATE: This issue does not occur using Chrome.

Use webdev as a daemon

As mentioned in #49 there is other ways of using the webdev package. I had quick a look at the source code of webdev and there is an "undocumented" command line option to use it as a daemon, just like someone can use flutter as described in the documentation.

pro:

  • If it's implemented with daemon, then jaspr shouldn't have webdev as a direct dependency (the user can activate it globally).
  • Already existing code could be reused, as it expects an executable/process right now.
  • The started process could be reused.

con:

  • Jaspr should implement it's own daemon, so the child process could be reused.
  • It looks like the daemon is only used for building, but not serving.

Maybe a mixed implementation could be used:

  • For building, jaspr could create it's own daemon based on flutter's and webdev's
  • For serving, it could call to webdev as a package

I wanted to create this issue to signal my finding, and start a conversation. Eventually I want to work on this and the mentioned issue's implementation, and I'm curious if creating such feature would be any help. ๐Ÿ˜„

Improve code documentation

There is still a lot of undocumented code in the core package. At least all important classes and methods should be documented using doc comments.

We should also further customize the generated api docs and add topics for organization using a dartdoc_options.yaml file.
See here for documentation and here for an example.

Add version option for jaspr cli

Jaspr cli should have -v, --version option for check version of jaspr which I am using.
Sometimes it can be usable :)

Now it is missing there:
image

Better document the sync generator syntax.

Developers that are new to dart are often not familiar with the sync generator syntax that is recommended in jaspr for custom components.

This syntax should be better documented as the 'recommended' but more advanced syntax, together with an alternative 'easy' syntax using normal list expressions.

Additionally, we could introduce SingleChildStatelessComponent and SingleChildStatefulComponent to have the same build signature as flutter widgets. This would allow an easier transition for developers coming from flutter.

Re-introduce consumer components to jaspr_riverpod

Currently there are no consumer components in jaspr_riverpod. These were removed in favor of the context extensions that are unique to jaspr.

In order to ease the transition from flutter_riverpod or help less experienced developers, we should re-introduce these components, but add documentation that these are not the recommended way. Maybe even add a deprecation annotation to leverage the lint system.

Invalid argument(s): A directory corresponding to fileSystemPath "...jaspr/experiments/minimal_app/lib/web" could not be found

I've checked out wip/v0.2 branch and whenever trying to run main.dart within the lib folder I end up seeing the following error.

Unhandled exception:
Invalid argument(s): A directory corresponding to fileSystemPath ".../jaspr/experiments/minimal_app/lib/web" could not be found
#0      createStaticHandler (package:shelf_static/src/static_handler.dart:50:5)
#1      staticFileHandler (package:jaspr/src/server/server_app.dart:273:7)
#2      staticFileHandler (package:jaspr/src/server/server_app.dart)
#3      _createHandler (package:jaspr/src/server/server_app.dart:278:38)
#4      ServerApp._run.<anonymous closure> (package:jaspr/src/server/server_app.dart:120:21)
#5      ServerApp._run.<anonymous closure> (package:jaspr/src/server/server_app.dart:117:22)
#6      new Future.microtask.<anonymous closure> (dart:async/future.dart:276:37)
#7      _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
#8      _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)
#9      _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:122:13)
#10     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:193:5)

I've noticed this same issue on master branch and have moved the main.dart file up 1 directory so it is located at ...jaspr/experiments/minimal_app/main.dart instead of ...jaspr/experiments/minimal_app/lib/main.dart which lets me run the project although I'm not sure if there's a reason not to do this

Serve jaspr app without ssr

As far as I understand there is no way to serve an app without SSR rendering. So if I don't want to support it I'll have to build an app while the development.

Can you please implement this feature?

Thanks a lot!

Classic template not working

Using wip/v0.2 branch, I've created a new Jaspr project using the classic template and the Hello World text does not appear.

Screenshots

What's shown in the browser

Screenshot 2022-09-13 at 12 31 39

DevTools

Screenshot 2022-09-13 at 12 31 53

Create new project error

During creating of new Jaspr project should be showed some better error:

image

For example:

"JasprProject" is not a valid project name.
Project name should be in snake_case format.

Or automatically convert my project name into correct name of pubspec.yaml

Error when trying the create jaspr project command.

`C:\Users\saifk\AppData\Local\Pub\Cache\bin>jaspr create jasp_web_app
Creating jasp_web_app using template web-simple...

.gitignore
analysis_options.yaml
CHANGELOG.md
pubspec.yaml
README.md
web\index.html
web\main.dart
web\styles.css

lib/app.dart
lib/app.dart

Resolving dependencies...
Null check operator used on a null value
package:pub/src/entrypoint.dart 157:60 Entrypoint.lockFilePath
package:pub/src/entrypoint.dart 100:21 Entrypoint._loadLockFile
package:pub/src/entrypoint.dart 97:42 Entrypoint.lockFile
package:pub/src/entrypoint.dart 286:21 Entrypoint.acquireDependencies.
package:pub/src/entrypoint.dart 280:68 Entrypoint.acquireDependencies.
package:pub/src/log.dart 428:18 progress
package:pub/src/entrypoint.dart 280:26 Entrypoint.acquireDependencies
package:pub/src/command/get.dart 52:22 GetCommand.runProtected
package:pub/src/command.dart 183:45 PubCommand.run.
package:pub/src/command.dart 183:33 PubCommand.run.
dart:async new Future.sync
package:pub/src/utils.dart 109:12 captureErrors.wrappedCallback
dart:async runZonedGuarded
package:pub/src/utils.dart 126:5 captureErrors
package:pub/src/command.dart 183:13 PubCommand.run
package:args/command_runner.dart 209:27 CommandRunner.runCommand
package:dartdev/dartdev.dart 231:30 DartdevRunner.runCommand
package:args/command_runner.dart 119:25 CommandRunner.run.
dart:async new Future.sync
package:args/command_runner.dart 119:14 CommandRunner.run
package:dartdev/dartdev.dart 66:29 runDartdev
C:\b\s\w\ir\cache\builder\sdk\pkg\dartdev\bin\dartdev.dart 11:9 main
This is an unexpected error. The full log and other details are collected in:

C:\Users\saifk\AppData\Local\Pub\Cache\log\pub_log.txt

Consider creating an issue on https://github.com/dart-lang/pub/issues/new
and attaching the relevant parts of that log file.
Null check operator used on a null value
#0 Entrypoint.lockFilePath (package:pub/src/entrypoint.dart:157:60)
#1 dumpTranscriptToFile (package:pub/src/log.dart:375:50)
#2 PubCommand.run (package:pub/src/command.dart:225:13)

#3 CommandRunner.runCommand (package:args/command_runner.dart:209:13)

#4 DartdevRunner.runCommand (package:dartdev/dartdev.dart:231:18)

#5 runDartdev (package:dartdev/dartdev.dart:66:16)

#6 main (file:///C:/b/s/w/ir/cache/builder/sdk/pkg/dartdev/bin/dartdev.dart:11:3)
`

Jaspr version confusing

Hello, I see that latest Jaspr release is:
image

But when I run:

dart pub global activate jaspr_cli

and then:

jaspr --version

I see
image

And in my generated project is:
image

It is very confusing what version now I am using..
Don't should be everywhere latest version: 0.3.0?

Hotreload doesn't work for main.dart file

For basic Jaspr project hotreload works for app.dart
but when I change anything in main.dart so hotreload doesn't work and I have to run jaspr serve again and reload page in browser manually.

`Text` component can broke an app

Hi, thanks for this package!

When the program is launched using serve command it breaks down with the exception below. However, it works fine when you build the app.

void main() {
  runApp(App());
}

class App extends StatelessComponent {
  @override
  Iterable<Component> build(BuildContext context) sync* {
    yield Text('Hello World');
  }
}

Exception:

Unhandled exception:
Bad state: No element
#0      List.last (dart:core-patch/growable_array.dart:348:5)
#1      _DomBuilder.text (package:domino/src/markup_impl.dart:177:28)
#2      TextElement.render (package:jaspr/src/framework/dom_component.dart:113:9)
#3      MultiChildElement.render (package:jaspr/src/framework/multi_child_element.dart:226:13)
#4      SingleChildElement.render (package:jaspr/src/framework/single_child_element.dart:64:13)
#5      BuildScheduler.render (package:jaspr/src/framework/components_binding.dart:75:11)
#6      renderMarkupIntoSink (package:domino/src/markup_impl.dart:33:12)
#7      renderMarkup (package:domino/src/markup_impl.dart:18:3)
#8      AppBinding.render (package:jaspr/src/bindings/server_bindings.dart:361:28)
<asynchronous suspension>
#9      renderHtml (package:jaspr/src/bindings/server_bindings.dart:318:14)
<asynchronous suspension>

Make AppBinding (and related) non-singleton.

This would be a diverge from the flutter api, but having the AppBinding a singleton has some problems both on the server and client:

  • On the server multiple requests must have a unique binding in order to generate correct html. Currently this is solved by using isolates for each request, but this is not supported in every environment like edge functions, and doesn't leave any room for performance considerations.
  • On the client multiple calls to runApp will use the same binding, which results in synched rendering loop and a shared global key registry, which is not really desired. So apps are not really independent.

Making this a non-singleton class would remove possible calls to AppBinding.instance, like addPostFrameCallback and similar methods. All those invocations must then go through a BuildContext, e.g. context.binding.

Jaspr with Tailwind

Description

Hello I experiment with Tailwind in Jaspr and due to Tailwind generator which generate my CSS styles into output file:
image

I have to create build folder by jaspr build command and run app with ./build/app

I would like to use jaspr serve command with hot reload for better development but I realize that jaspr serve
doesn't create the build folder with javascript sources.

Can you please add this functionality?
For example add a flag jaspr serve --with-build?

Additional Context

image

`build` method middleware or wrappers to support MobX, hooks, Pods and other framework-wide functionalities

Hi, thanks for your work!

This may be a weird feature request which will make the package a bit different from Flutter and there may be performance considerations. However, the usability improvements would be great, in my opinion.

The idea is to provide a middleware for rendering Components, a function that wraps the executions of all build methods, to do various things.

For example, for registering and tracking dependencies that rebuild the Widget when they change. I believe the javascript framework https://www.solidjs.com/ is based on this concept, similar to MobX's tracking of dependencies. Through testing the package I have implemented some MobX and hooks bindings for Components. In this file you can see the main hooks logic and also the tracking of dependencies within build methods of StatelessObserverComponent similar to the one in package:flutter_mobx, however this one also supports hooks. In Flutter this is a problem it's difficult to use package:flutter_mobx and package:flutter_hooks (two packages implementing Elements) without nesting that the composing of middleware or multiple wrappers can solve. There are no automatic test, but I have tested them manually and you can try the deployed html in gh-pages. More hooks, using the same infrastructure, are implemented in this hooks file.

This is similar to some work I did in a package:deact fork. However, in that fork a kind of middleware or wrapper is implemented for all build methods, not only for those in StatelessObserverWidget. Which is nicer, since one does not worry about using the appropriate Widget (every build method is being tracked). You would not have to worry where to place them, one would register the middleware for the whole app (or a section) and use observables (or hooks) that are tracked by the middleware. They would all be tracked and rebuild the widgets in the places where the observables are used. There are perhaps some performance implications. Maybe some kind of similar middleware or wrapper for build functions could be provided as API. In this case, all that is needed is a value that saves state in the element and can be disposed (HookCtx), the markNeedsBuild method and a function that executes the build method. As shown in this code:

List<Component> _mobxHooksWrapper(
  HookCtx ctx,
  void Function() markNeedsBuild,
  List<Component> Function() next,
) {
  final previousCtx = _ctx;
  _ctx = ctx;
  final List<Component> children = useTrack(
    next,
    markNeedsBuild,
    name: ctx.hashCode.toString(),
  );
  _ctx = previousCtx;
  return children;
}

_mobxHooksWrapper is executed on every build and provides MobX tracking with useTrack and hooks support with the HookCtx that is saved in the ComponentElement.

I the repo, I am always using the implemented StatelessObserverComponent or StatefulObserverComponent since they provide hooks and MobX tracking. To make sure I use them I import package:jaspr though a prelude.dart file that overrides StatelessComponent to StatelessObserverComponent.

Other use cases may include some kind of profiling, however there are probably other ways to do it too. Maybe some kind of dynamic (can be turned on and off) profiling of certain parts of the Widget tree though similar middleware.

Maybe unrelated, but maybe some API for dependency injection can be explored though a middleware also. Within the repo there is also an implementation of dependency injection, similar to package:riverpod. For watching, I am relying on MobX's dependency tracking, the pod's value in a pod scope is immutable. However, their properties may be mutable and, if they are MobX observables, everywhere they are used, they are being tracked (provided one uses StatelessObserverComponent). For disposing, the values are disposed when the scope is disposed which is created using the _InheritedPodScope InheritedWidget. One could also create scopes outside of jaspr, but within jasper one reads the pod values with an extension over BuildContext that implements a T pod<T>(Pod<T> pod) method that reads the closest _InheritedPodScope and retrieves the value. Pods can be global or scoped. If they are global a single instance will be created for all scopes within the tree and will be disposed when the whole tree is disposed. If it's not global, then it will be created within a subtree, all children of the scope will be have access to the same instance, but there could be other instances in other scopes (subtrees).

In this case maybe a middleware could be implemented to support the autoDispose feature in riverpod, where the dependencies are saved within the element and when the Pod's Element dependencies are disposed, then the Pod is disposed.

In general, this would allow for the framework to be extensible and to test or evaluate framework-wide functionalities that may be implemented natively by the framework in the future, or maybe just leave them as separate packages.

Thanks!

Add utility methods / components for common html elements

Discussion related to #13

Status quo

The current way of defining html elements is by using DomComponent and providing a tag. This leaves the most freedom to developers, but can get verbose quickly when composing uis:

This:

return DomComponent(
  tag: 'div',
  children: [
    DomComponent(
      tag: 'h1', 
      child: Text('This is a title'),
    ),
    DomComponent(
      tag: 'p',
      children: [
         Text('Hello '),
         DomComponent(
           tag: 'b',
           child: Text('World!'),
        ),
      ],
    ),
  ],
);

is unarguably a lot more verbose than standard html:

<div>
  <h1>This is a title</h1>
  <p>Hello <b>World!</b></p>
</div>

Proposal / Discussion

While DomComponent should stay as the low-level base for all html elements, we need a more simple way to use common html elements like div and others. This should be added on top of the core framework and be optional to use by the developer.

What should such an element look like?

There are two ways we could define such elements:

  1. As their own StatelessComponent
  2. As methods returning a DomComponent

Separate components (1) would define a class for each element, take any arguments in the constructor and return a DomComponent in the build() method. Since classes use PascalCase for their naming, we either

  • (a) capitalize the tag name (Div, P, H1), or
  • (b) extend the tags to their more descriptive names (Division, Paragraph, Heading1).

Both are not really ideal, since (a) would have a bunch of single-letter class names and (b) would be again quite verbose for a lot of elements. Additionally, having separate components introduces a lot of extra depth into the component tree when building uis (2 components for each element, the elements component + the dom component), which will have some performance implications for larger apps.

Element methods (2) would provide a way to define shorthands for common elements without the overhead of additional components. A method can also have custom properties specific for the element and would just return a single DomComponent with the static tag and provided attributes. Methods also have lower-case names and could directly mirror the original html tags (div(), p(), h1()), keeping the code concise and appearing similar to original html.

Which elements are included?

Of course we want as many elements included as possible, however this might be a challenge as there are a lot of different elements in the html spec.

When writing this manually we would have to start with a few most common elements and expand the list over time. However this would also mean a lot of maintenance when the component api or properties of DomComponent change (which might happen since jaspr is very early stage).

A better and more easy way would be to generate the methods and their bodies for each html element automatically. A good example would be domino_html which does exactly this for the domino package. It uses a script that crawls developer.mozilla.org/en-US/docs/Web/HTML/Element and developer.mozilla.org/en-US/docs/Web/HTML/Attributes to get all html elements and their attributes. Modifying this script to output jaspr-compatible code should be feasible. There are however some additional considerations to make:

  • Some of the element have many uncommon attributes. These get very verbose and make the parameter list unclear and convoluted. A possibility would be to not include global attributes as defined by the attributes page.
  • Some attributes have custom types other than String (mainly boolean, int or enums). It would be cool to provide them as typed attributes (especially the enums), but I don't know right now how to get this information, since it is not specified in the attributes page.

Address already in use error

After run command: dart run jaspr serve

martin at probook-pc my_web_app >>> dart run jaspr serve -v
Starting jaspr development server...
[INFO] Building new asset graph completed, took 1.5s
[INFO] Checking for unexpected pre-existing outputs. completed, took 1ms
[INFO] Serving `web` on http://127.0.0.1:5467
[INFO] Generating SDK summary completed, took 3.8s
[INFO] Running build completed, took 10.7s
[INFO] Caching finalized dependency graph completed, took 269ms
[INFO] Succeeded after 10.9s with 430 outputs (2540 actions)
[INFO] ------------------------------------------------------------------------
Observatory listening on http://127.0.0.1:8181/Cok83YcIVRk=/
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/Cok83YcIVRk=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A8181%2FCok83YcIVRk%3D%2Fws
lib/main.dart: Warning: Interpreting this as package URI, 'package:my_web_app/main.dart'.
Unhandled exception:
SocketException: Failed to create server socket (OS Error: Address already in use, errno = 98), address = 0.0.0.0, port = 8080
#0      _NativeSocket.bind (dart:io-patch/socket_patch.dart:996:7)
<asynchronous suspension>
#1      serve (package:shelf/shelf_io.dart:51:16)
<asynchronous suspension>
#2      _reload (package:jaspr/src/bindings/server_bindings.dart:217:17)
<asynchronous suspension>
#3      ServerApp._run.<anonymous closure> (package:jaspr/src/bindings/server_bindings.dart:108:9)
<asynchronous suspension>

I see my port 8080 is already in use. But in help I don't see any info how can I set some different port:

martin at probook-pc my_web_app >>> jaspr serve -p 4000
Could not find an option or flag "-p".

Usage: jaspr serve [arguments]
-h, --help                      Print this usage information.
-i, --input                     Specify the input file for the web app.
-m, --mode                      Sets the reload/refresh mode.

          [refresh]             Performs a full page refresh and server reload
          [reload] (default)    Reloads js modules without server reload (loses current state)

-d, --[no-]debug                Serves the app in debug mode.
-v, --[no-]verbose              Enable verbose logging.

Run "jaspr help" to see global options.

jaspr serve should have some argument for change this default settings.

Issue executing "jaspr serve" on a project created with the "client" Jaspr template

I've successfully created and tested a template project for basic, classic, islands and server however whenever executing jaspr serve on a project created with the client template, this is the error log shown.

โžœ  sorare_agent_landing_page jaspr serve
โœ“ Starting jaspr development server in debug mode... (5.6s)
The Dart VM service is listening on http://127.0.0.1:8181/Ecnzrek4gHo=/
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/Ecnzrek4gHo=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A8181%2FEcnzrek4gHo%3D%2Fws
../../../.pub-cache/hosted/pub.dev/jaspr-0.2.0+4/lib/src/browser/browser_binding.dart:3:8: Error: Dart library 'dart:html' is not available on this platform.
import 'dart:html';
       ^
Context: The unavailable library 'dart:html' is imported through these packages:

    main.dart => package:jaspr => dart:html

Detailed import paths for (some of) the these imports:

    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/browser_binding.dart => dart:html
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/dom_renderer.dart => dart:html
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/islands.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => dart:html
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/islands.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/dom_renderer.dart => dart:html
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/native_interop.dart => dart:html
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/native_interop.dart => package:jaspr/src/browser/dom_renderer.dart => dart:html
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => dart:html
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/dom_renderer.dart => dart:html

../../../.pub-cache/hosted/pub.dev/jaspr-0.2.0+4/lib/src/browser/js_data.dart:5:8: Error: Dart library 'dart:js' is not available on this platform.
import 'dart:js';
       ^
Context: The unavailable library 'dart:js' is imported through these packages:

    main.dart => package:jaspr => dart:js
    main.dart => package:jaspr => package:js => dart:js

Detailed import paths for (some of) the these imports:

    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/js_data.dart => dart:js
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/js_data.dart => package:js/js.dart => dart:js
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/islands.dart => package:jaspr/src/browser/js_data.dart => dart:js
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/islands.dart => package:jaspr/src/browser/js_data.dart => package:js/js.dart => dart:js
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/islands.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/js_data.dart => dart:js
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/islands.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/js_data.dart => package:js/js.dart => dart:js
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/js_data.dart => dart:js
    main.dart => package:jaspr/browser.dart => package:jaspr/src/browser/run_app.dart => package:jaspr/src/browser/browser_binding.dart => package:jaspr/src/browser/js_data.dart => package:js/js.dart => dart:js

../../../.pub-cache/hosted/pub.dev/jaspr-0.2.0+4/lib/src/browser/native_interop.dart:1:8: Error: Dart library 'dart:html' is not available on this platform.
import 'dart:html';
       ^
../../../.pub-cache/hosted/pub.dev/jaspr-0.2.0+4/lib/src/browser/dom_renderer.dart:2:8: Error: Dart library 'dart:html' is not available on this platform.
import 'dart:html';
       ^
../../../.pub-cache/hosted/pub.dev/jaspr-0.2.0+4/lib/src/browser/dom_renderer.dart:3:8: Error: Dart library 'dart:html' is not available on this platform.
import 'dart:html' as html show Element, Text;
       ^
../../../.pub-cache/hosted/pub.dev/js-0.6.7/lib/js.dart:10:1: Error: Dart library 'dart:js' is not available on this platform.
export 'dart:js' show allowInterop, allowInteropCaptureThis;
^
../../../.pub-cache/hosted/pub.dev/jaspr-0.2.0+4/lib/src/browser/native_interop.dart:7:3: Error: Type 'Node' not found.
  Node? get nativeElement => data.node;
  ^^^^
../../../.pub-cache/hosted/pub.dev/jaspr-0.2.0+4/lib/src/browser/dom_renderer.dart:9:3: Error: Type 'Node' not found.
  Node? node;
  ^^^^

jaspr_cli version
0.1.0

Dart SDK version
2.19.5 (stable) (Mon Mar 20 17:09:37 2023 +0000) on "macos_arm64"

Cannot run jaspr in Linux Mint

Hello I am trying run in Linux Mint command: dart run jaspr serve -v

But it return me:

martin at probook-pc my_web_app >>> dart run jaspr serve -v
Building package executable...
Built jaspr:jaspr.
Starting jaspr development server...
Failed to build webdev:webdev:
/home/martin/.pub-cache/hosted/pub.dartlang.org/dwds-12.1.1/lib/src/services/chrome_proxy_service.dart:540:24: Error: The method 'ChromeProxyService.getSourceReport' has fewer named arguments than those of overridden method 'VmServiceInterface.getSourceReport'.
  Future<SourceReport> getSourceReport(String isolateId, List<String> reports,
                       ^
/home/martin/.pub-cache/hosted/pub.dartlang.org/vm_service-8.3.0/lib/src/vm_service.dart:846:24: Context: This is the overridden method ('getSourceReport').
  Future<SourceReport> getSourceReport(
                       ^
/home/martin/.pub-cache/hosted/pub.dartlang.org/dwds-12.1.1/lib/src/services/chrome_proxy_service.dart:540:24: Error: The method 'ChromeProxyService.getSourceReport' doesn't have the named parameter 'libraryFilters' of overridden method 'VmServiceInterface.getSourceReport'.
  Future<SourceReport> getSourceReport(String isolateId, List<String> reports,
                       ^
/home/martin/.pub-cache/hosted/pub.dartlang.org/vm_service-8.3.0/lib/src/vm_service.dart:846:24: Context: This is the overridden method ('getSourceReport').
  Future<SourceReport> getSourceReport(
                       ^
Failed to build webdev:webdev:
/home/martin/.pub-cache/hosted/pub.dartlang.org/dwds-12.1.1/lib/src/services/chrome_proxy_service.dart:540:24: Error: The method 'ChromeProxyService.getSourceReport' has fewer named arguments than those of overridden method 'VmServiceInterface.getSourceReport'.
  Future<SourceReport> getSourceReport(String isolateId, List<String> reports,
                       ^
/home/martin/.pub-cache/hosted/pub.dartlang.org/vm_service-8.3.0/lib/src/vm_service.dart:846:24: Context: This is the overridden method ('getSourceReport').
  Future<SourceReport> getSourceReport(
                       ^
/home/martin/.pub-cache/hosted/pub.dartlang.org/dwds-12.1.1/lib/src/services/chrome_proxy_service.dart:540:24: Error: The method 'ChromeProxyService.getSourceReport' doesn't have the named parameter 'libraryFilters' of overridden method 'VmServiceInterface.getSourceReport'.
  Future<SourceReport> getSourceReport(String isolateId, List<String> reports,
                       ^
/home/martin/.pub-cache/hosted/pub.dartlang.org/vm_service-8.3.0/lib/src/vm_service.dart:846:24: Context: This is the overridden method ('getSourceReport').
  Future<SourceReport> getSourceReport(
                       ^

How can I fix it?

My Dart version is:

Dart SDK version: 2.16.1 (stable) (Tue Feb 8 12:02:33 2022 +0100) on "linux_x64"

And linux version:

Distributor ID: LinuxMint
Description:    Linux Mint 19.3 Tricia
Release:        19.3
Codename:       tricia

And here are my dependencies:

martin at probook-pc my_web_app >>> dart pub upgrade
Resolving dependencies... (2.4s)
  _fe_analyzer_shared 34.0.0 (40.0.0 available)
  analyzer 3.2.0 (4.1.0 available)
  archive 3.3.0
  args 2.3.1
  async 2.9.0
  bazel_worker 1.0.1
  binary_codec 2.0.3
  browser_launcher 1.1.0
  build 2.3.0
  build_config 1.0.0
  build_daemon 3.1.0
  build_modules 4.0.4 (4.0.5 available)
  build_resolvers 2.0.6 (2.0.9 available)
  build_runner 2.1.11
  build_runner_core 7.2.3
  build_web_compilers 3.2.3
  built_collection 5.1.1
  built_value 8.3.2
  checked_yaml 2.0.1
  cli_util 0.3.5
  code_builder 4.1.0
  collection 1.16.0
  convert 3.0.2
  crypto 3.0.2
  csslib 0.17.2
  dart_style 2.2.1 (2.2.3 available)
  dds 2.2.1
  dds_service_extensions 1.3.0
  devtools_shared 2.13.1 (2.14.0 available)
  domino 0.8.3
  dwds 12.1.1 (14.0.3 available)
  file 6.1.2
  fixnum 1.0.1
  frontend_server_client 2.1.3
  glob 2.0.2
  graphs 2.1.0
  hotreloader 3.0.4
  html 0.15.0
  http 0.13.4
  http_multi_server 3.2.0
  http_parser 4.0.1
  io 1.0.3
  jaspr 0.1.3
  js 0.6.4
  json_annotation 4.5.0
  json_rpc_2 3.0.1
  lints 1.0.1 (2.0.0 available)
  logging 1.0.2
  matcher 0.12.11
  meta 1.8.0
  mime 1.0.2
  package_config 2.0.2
  path 1.8.2
  pedantic 1.11.1
  pool 1.5.0
  protobuf 2.0.1
  pub_semver 2.1.1
  pubspec_parse 1.2.0
  scratch_space 1.0.1
  shelf 1.3.0
  shelf_packages_handler 3.0.0
  shelf_proxy 1.0.1
  shelf_static 1.1.0
  shelf_web_socket 1.0.1
  source_maps 0.10.10
  source_span 1.9.0
  sse 4.1.0
  stack_trace 1.10.0
  stream_channel 2.1.0
  stream_transform 2.0.0
  string_scanner 1.1.1
  term_glyph 1.2.0
  timing 1.0.0
  typed_data 1.3.1
  usage 4.0.2
  uuid 3.0.6
  vm_service 8.3.0
  watcher 1.0.1
  web_socket_channel 2.2.0
  webdev 2.7.8 (2.7.9 available)
  webkit_inspection_protocol 1.1.0
  yaml 3.1.1
No dependencies changed.
9 packages have newer versions incompatible with dependency constraints.
Try `dart pub outdated` for more information.

input html element's value is not set when an empty String is used

Hi,

I found that when I pass an empty String as an attribute to an input element, the text is not cleared. The value of the element is not overridden. However, if the value is a non empty String, the input element's value does change. At the moment, I am saving the dart:html's InputElement reference to clear and update the value, like a Flutter TextEditingController.

Since dart:html cannot be used in the backend, the value is dynamic, could this be resolved in some way? maybe by providing a wrapper for common use cases (a typed event perhaps) or a TextEditingController like API? Or should users use universal_html and cast everything like (event as html.Event).target as html.InputElement?

For the wip/v0.2 branch, I was also wondering whether a void Function(String value) onValueChange argument in the input component could be used as API, or maybe instead of String as argument it could be a specific type (for number or date inputs) or a typed event (with the new value, the InputElement and the raw event).

I tried it in the current main and wip/v0.2 branch.

This is related to a discussion in the rad framework erlage/rad#13.

Thanks!

Rendering interface for Component libraries and shared logic in Dart web rendering frameworks

Hi, thanks for you work!

I have been building the web bootstrap components bindings in this repo. At the moment it supports three different frameworks: deact, jaspr and rad. I have implemented bindings for each of the frameworks separately in different packages. You can view all the implemented bootstrap components in the deployed page, that page renders the components with package:deact. Another deact example is this one and a jaspr example is this one. At the moment, I have not implemented any rad examples.

Maybe it would be nice to collaborate on a shared interface between the Dart web frameworks, perhaps something similar to what bootstrap_dart is doing, but where the libraries implement them natively. You can view the Dart interface in the readme of the repo.

Perhaps a "web_rendering_interface" package that exposes interfaces like Ref, State, BuildContext, Component, some base hooks, and the ability to render HTML elements, text nodes and fragments (List of Components) could be used. The rendering packages (deact, jaspr or rad) implement these interfaces and the component packages (or logic packages such as for hooks or routing) use those interfaces.

At the moment, bootstrap_dart uses a global BoostrapRenderer instance that has to be overridden to use the renderer for each package like this example that executes this function. The returned components are of type dynamic, which works, but maybe isn't great. BoostrapRenderer has a generic type parameter for the component type which could be used to improve type safety. For type safety, the BoostrapRenderer in the repo could use extensions to implement the components and the BoostrapRenderer instance could be used directly (boostrapRenderer.card(...) to return a Card bootstrap component). The extensions would need to be imported since they contain the implementation for each component. Or perhaps we could expose a Component interface in "web_rendering_interface" so that the component libraries return an instance of that interface. Or reimplement each component in all the packages that interface the rendering framework with the component libraries, perhaps with code generation to maintain documentation and the API synched.

This would allow for the component libraries to use the same code and interface with multiple different rendering libraries. As well as perhaps share hooks between different frameworks. This could also be extended to other functionalities like server side rendering, routing or dependency injection. Where the routing logic could be used for every rendering package.

Related to erlage/rad#21 in the rad package github repo. The content of the issue is the same.

Thanks for reading!

fix: jaspr_router calls onUnknownRoute before navigating to any route

Description

I'm using jaspr_router and whenever I navigate to another page or whenever a page is loading, the component from onUknownRoute callback is shown.

Steps To Reproduce

@app
class App extends StatelessComponent with _$App {
  @override
  Iterable<Component> build(BuildContext context) sync* {
    yield Router(
      routes: [
        Route('/', (context) => [HomePage()]),
        Route('/about', (context) => [AboutPage()]),
      ],
      onUnknownRoute: (path, context) => NotFoundPage(),
    );
  }
}
Screen.Recording.2023-04-26.at.12.23.25.mov

Add example for data fetching and synchronization

Data fetching and sync is an important aspect for most websites and we should have a well-documented example for this.

Can be done together with some other feature or use-case.

This may include:

  • separating code between server and client (the import problem)
  • fetching data on the server (e.g. from an api or database)
  • syncing this data with the client
  • optionally modifying or writing data back to the server (crud style)
  • using non-trivial models with serialization to json (best using dart_mappable package)

Bug in HTML Entity characters

I want to add into my page some &nbsp; or other HTML entity characters.

I have this code:

import 'package:jaspr/components.dart';
import 'package:jaspr/jaspr.dart';
import 'package:jaspr/styles.dart';

class BasicApp extends StatelessComponent {
  @override
  Iterable<Component> build(BuildContext context) sync* {
    yield Header(text: 'Hello World', size: HeaderSize.h2);

    yield RichText(children: [
      TextSpan(
        text: "Don't have an account?",
        styles: Styles.text(
          color: Color.named('black'),
          fontWeight: FontWeight.w500,
          fontSize: 14.pt,
        ),
      ),
      Text("&nbsp;", rawHtml: false),
      TextSpan(
        text: "Sign up",
        styles: Styles.text(
          color: Color.named('blue'),
          fontWeight: FontWeight.w500,
          fontSize: 14.pt,
        ),
      )
    ]);
  }
}

And after build I see this:
image

When I set rawHtml to true:
image
so I don't see my previous component there:
image

Is some bug in builder?

Code of new jaspr components which I am using in example above is here: https://github.com/mjablecnik/jaspr/tree/jaspr-components/packages/jaspr/lib/src/ui/components

Styling in jaspr

When looking at #13 I very much liked the concept of a Style class which bundles css properties in a simple way. The advantages of such a class over a raw Map<String, String are really good:

  • strongly typed properties
    • instead of everything being a String, you can have types for numbers, enums, units or custom classes for things like Color
  • IDE support
    • you get hints which values a property can have and how to construct them
    • properties can be documented and viewed from inside the IDE (no googling css properties)
    • e.g. for padding having the EdgeInsets class with separate constructors for .only, .symmetric or .all translates well to the different shorthand versions
  • you only write dart code instead of having to write css
    • this is especially useful for flutter devs or others that are not fluid in css

Proposal / Discussion

When implementing such a class, we need to make sure that it is easy to use and understand. It should be possible to compose, change, and combine styles in an easy way.

Grouping Styles

Having a single Style class with all implemented properties as fields will get way to large very quickly. This negates the positive effect of having IDE support, since you get a very long and convoluted list of properties. I like the way how #13 groups semantically related styles together into sub-classes, like TextStyle, BackgroundStyle or BoxStyle.

In the pr, combining those style groups is done through the MultipleStyle class, which takes a list of BaseStyles. I don't really like this approach since it introduces an additional class (or two with the BaseStyle class) just to allow for settings styles from different semantic groups. Also I think this is not ideal since the user could e.g. put two TextStyles which would not make sense. Also the user does not have help from the IDE in which style groups exist and can be used.


Instead an approach would be to have the Style class accept instances of the grouped sub-classes, something like:

var style = Style(
  text: TextStyle(...),
  background: BackgroundStyle(...),
  box: BoxStyle(...),
);

A disadvantage of this approach is that it is not extensible by the user, while with the MultipleStyle the user could create an own subclass of the BaseStyle class to be passed to the styling list. Also it lacks a way of providing raw styles in order to account for properties that are missing from the existing style classes (could be solved with an additional Map<String, String> raw property).


Another approach could be to use a builder-like pattern to compose styles. Instead or providing style properties through the constructor, there could be methods on the Style class to incrementally compose a style:

var style = Style()
  .text(...)
  .background(...)
  .box(...)
  .raw(...);

Internally this would probably just keep a Map<String, String> instead of keeping instances of separate classes.

This would allow for easy extension by the user through darts extension methods. It works also great with IDE code completion. A disadvantage would be that this would not allow to use styles as a const variable, which might be desirable when you have a set of standard styles you want to use in your app. It's also not very "Fluttery".

Modifying / Composing Styles

With the first approach, in order to modify the style instance the Style class as well as all sub-classes would have to implement a copyWith method. Again this isn't really extensible by the user and double the work to maintain. Merging two styles would also not be easy and requires to merge all sub-classes individually.

The second approach would be modifiable by default, since you could just call e.g. the .text() method again which would override any existing text-styles. It would also be easier to merge two styles by just merging the internal Maps of both.

Both approaches would still be immutable. With "modifying" I mean returning a new instance with adjusted styles, not mutating the existing instance.

A third way

While I like the second approach more because of its advantages, it does not feel very "Fluttery". So I would like to explore a third option combining all the above.

There could be separate sub-classes for semantic style groups, but those are private and hidden behind named constructors of the actual Style class. This would then look like Style.text(...), Style.background(...), Style.raw(...), etc. for the user, which has IDE support for code completion. The user could then still create a custom sub-class extending Style.

Combining styles could be done through a Style.combine(Iterable<Style> styles) constructor, removing the need for a separate class to combine multiple styles. Modifying a style would be done through the same method, by combining the original style with a new style, overriding the existing properties.

Together this would look like:

var backgroundStyle = Style.background(...);

var style = Style.combine([
  backgroundStyle,
  Style.text(...),
  Style.box(...),
  Style.raw({'color': 'blue'}),
  MyCustomStyle(),
]);

A component (e.g. 'DomComponent') would then accept a single Style style property.

Responsive web support

I think about how to support creating web responsive pages in Jaspr.
I realized that we didn't implement @media rules:

@media screen and (max-width: 900px) and (min-width: 600px), (min-width: 1100px) {
  div.example {
    font-size: 50px;
    padding: 50px;
    border: 8px solid black;
    background: yellow;
  }
}

Which are important for responsive web pages.
Media rules can be only in CSS files or <style> tags. It is not possible to put it inline.

So probably we will need to add new special type of rule: MediaRule which will take @media parameters
and add it into <style> element where every styles rule will describe styles for various size of app (phone, tablet, desktop, etc.):

class ExampleApp extends StatelessComponent {

    @override
    Iterable<Component> build(BuildContext context) sync* {

        yield Container(
            classes: ['test1'], 
            child: Text('Hello world'),
        );

        yield Container(
            classes: ['test2'], 
            child: Text('Hello world123'),
        );

        yield Style(rules: [
            MediaRule(
                maxWidth: 640.px, 
                rules: [
                    StyleRule(
                        selector: Selector.className('test1'), 
                        styles: Styles.combine([
                            Styles.text(color: Colors.red),
                            Styles.background(color: Colors.blue),
                            Styles.box(display: Display.block),
                        ]),
                    ),
                    StyleRule(
                        selector: Selector.className('test2'), 
                        styles: Styles.box(display: Display.none),
                    ),
                ],
            ),
            MediaRule(
                minWidth: 640.px, 
                maxWidth: 860.px, 
                rules: [
                    StyleRule(
                        selector: Selector.className('test1'), 
                        styles: Styles.combine([
                            Styles.text(color: Colors.orange),
                            Styles.background(color: Colors.green),
                            Styles.box(display: Display.block),
                        ]),
                    ),
                    StyleRule(
                        selector: Selector.className('test2'), 
                        styles: Styles.box(display: Display.none),
                    ),
                ],
            ),
            MediaRule(
                minWidth: 860.px, 
                maxWidth: 1960.px, 
                rules: [
                    StyleRule(
                        selector: Selector.className('test1'), 
                        styles: Styles.box(display: Display.none),
                    ),
                    StyleRule(
                        selector: Selector.className('test2'), 
                        styles: Styles.combine([
                            Styles.text(color: Colors.orange),
                            Styles.background(color: Colors.green),
                            Styles.box(width: 800.px, display: Display.block),
                        ]),
                    ),
                ],
            ),
        ]);

    }
}

@schultek What do you think about it?

How to generate static website?

Hi schultek, is it appropriate to use jaspr to generate static pages, like hugo or jekyll did?

Using StatelessComponent to build gui, and using dart to do interaction, and data was injected through InheritageComponent, and also it could handle raw html for final page generation, e.g. the raw html generated by markdown files. Seems we need templatize dart page files?

It would be a marvelous feature if it could be implemented.

Use webdev as package

The jaspr cli currently uses webdev as a separate child process. However we want to switch to using webdev directly as a package by directly calling its implementation in the dart code.

This hopefully also fixes #17

jaspr build --no-ssr fails for client template

I've checked out wip/v0.2 branch and added pubspec_overrides.yaml file to the generated client template which looks like the following:

dependency_overrides:
  jaspr:
    path: ../../packages/jaspr

Here's the logs which contains the error message after executing dart run jaspr build --no-ssr

client git:(wip/v0.2) โœ— dart run jaspr build --no-ssr
Building package executable... (1.2s)
Built jaspr:jaspr.
[INFO] Reading cached asset graph completed, took 141ms
[INFO] Checking for updates since last build completed, took 378ms
[INFO] Running build completed, took 78ms
[INFO] Caching finalized dependency graph completed, took 127ms
[SEVERE] build_web_compilers:entrypoint on web/main.dart (cached): ExitCode:1
StdOut:
Info: Compiling with sound null safety
org-dartlang-app:///packages/jaspr/src/bindings/browser_bindings.dart@6035+17:
Error: The getter 'kBrowserDebugMode' isn't defined for the class 'BrowserDomRenderer'.
 - 'BrowserDomRenderer' is from 'package:jaspr/src/bindings/browser_bindings.dart' ('org-dartlang-app:///packages/jaspr/src/bindings/browser_bindings.dart').
Error: Compilation failed.

StdErr:


[SEVERE] Failed after 218ms
[SEVERE] FailureType: 1

jaspr serve ignore port argument

I try to use port 8089 because port 8080 is already in use by other service.
Instead of using port 8089 jaspr choose 5467 by itself.

โžœ  jaspr_web_app jaspr serve -p 8089 -v
Starting jaspr development server...
[INFO] Reading cached asset graph completed, took 233ms
[INFO] Checking for updates since last build completed, took 458ms
[INFO] Serving `web` on http://127.0.0.1:5467
[INFO] Running build completed, took 116ms
[INFO] Caching finalized dependency graph completed, took 213ms
[INFO] Succeeded after 335ms with 0 outputs (0 actions)
[INFO] ------------------------------------------------------------------------
The Dart VM service is listening on http://127.0.0.1:8181/hLKGtN42SPc=/
The Dart DevTools debugger and profiler is available at: http://127.0.0.1:8181/hLKGtN42SPc=/devtools/#/?uri=ws%3A%2F%2F127.0.0.1%3A8181%2FhLKGtN42SPc%3D%2Fws
[INFO] Hot reload is enabled.
[INFO] Running app in debug mode
[INFO] Serving at http://0.0.0.0:8089
[INFO] Received request for entrypoint at http://127.0.0.1:5467/main.dart.bootstrap.j
[INFO] Injected debugging metadata for entrypoint at http://127.0.0.1:5467/main.dart.bootstrap.js

Support multiple apps (island architecture)

Currently, there can only ever be one app which is started by a single call to runApp(). This is fine for the server, but in the browser this has a few drawbacks:

  • The whole app has to be shipped to the client, including static parts without any interactivity.
  • This results in a larger js file and longer time to interactivity.
  • The page must be hydrated from the root up and synchronously for all parts.

Jaspr on the client should be able to handle multiple parallel applications bound to different parts of the page. It then should be possible to call runApp() multiple times with different attachment targets, e.g.:

void main() {
  runApp(Header(), attachTo: '#header');
  runApp(Sidebar(), attachTo: '#sidebar');
  runApp(Content(), attachTo: '#content');
}

This would start three different app instances (three separate component trees) bound to different parts of the app (here: header, sidebar and content areas).


This change would align very well the the 'Islands Architecture' pattern that became popular again with recent web development trends. Islands are interactive parts of the page, that can be loaded and hydrated separately from each other.

Goal: When supporting multiple apps, jaspr should provide the necessary tools to easily use the islands architecture pattern in a project.


Useful links:

https://www.patterns.dev/posts/islands-architecture/
https://jasonformat.com/islands-architecture/

Unable to build on windows

PS C:\Users\andre\Desktop> jaspr create try_jaspr
Creating try_jaspr using template web-simple...

  .gitignore
  analysis_options.yaml
  CHANGELOG.md
  pubspec.yaml
  README.md
  web\index.html
  web\main.dart
  web\styles.css

  lib/app.dart
  lib/app.dart

Resolving dependencies...
Changed 81 dependencies!

Created project try_jaspr in try_jaspr! In order to get started, run the following commands:

  cd try_jaspr
  jaspr serve

PS C:\Users\andre\Desktop> cd try_jaspr
PS C:\Users\andre\Desktop\try_jaspr> jaspr build
Unhandled exception:
Unsupported operation: Cannot extract a file path from a c URI
#0      _Uri.toFilePath (dart:core/uri.dart:2825:7)
#1      new Directory.fromUri (dart:io/directory.dart:129:59)
#2      BuildCommand.run (file:///C:/Users/andre/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/jaspr-0.1.2/bin/jaspr.dart:237:25)
#3      CommandRunner.runCommand (package:args/command_runner.dart:209:27)
#4      CommandRunner.run.<anonymous closure> (package:args/command_runner.dart:119:25)
#5      new Future.sync (dart:async/future.dart:301:31)
#6      CommandRunner.run (package:args/command_runner.dart:119:14)
#7      main (file:///C:/Users/andre/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/jaspr-0.1.2/bin/jaspr.dart:23:31)
#8      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:295:32)
#9      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
Unhandled exception:
Unsupported operation: Cannot extract a file path from a c URI
#0      _Uri.toFilePath (dart:core/uri.dart:2825:7)
#1      new Directory.fromUri (dart:io/directory.dart:129:59)
#2      BuildCommand.run (file:///C:/Users/andre/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/jaspr-0.1.2/bin/jaspr.dart:237:25)
#3      CommandRunner.runCommand (package:args/command_runner.dart:209:27)
#4      CommandRunner.run.<anonymous closure> (package:args/command_runner.dart:119:25)
#5      new Future.sync (dart:async/future.dart:301:31)
#6      CommandRunner.run (package:args/command_runner.dart:119:14)
#7      main (file:///C:/Users/andre/AppData/Local/Pub/Cache/hosted/pub.dartlang.org/jaspr-0.1.2/bin/jaspr.dart:23:31)
#8      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:295:32)
#9      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
PS C:\Users\andre\Desktop\try_jaspr>

Add example using aws lambda

A jaspr app that can be deployed to aws lambda using their dart runtime.

Can be done together with some other feature or use-case.

This may include:

  • any sort of setup needed to run on aws lambda
  • building and deployment instructions (e.g. in README) for aws lambda
  • (optionally) integrating with some other aws product on the server (e.g. database)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.