zalando / tailor Goto Github PK
View Code? Open in Web Editor NEWA streaming layout service for front-end microservices
Home Page: http://www.mosaic9.org
License: MIT License
A streaming layout service for front-end microservices
Home Page: http://www.mosaic9.org
License: MIT License
I tried to install from a freshly cloned repository and always getting this error message:
npm i node-tailor --save
npm ERR! Linux 4.4.0-28-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "i" "node-tailor" "--save"
npm ERR! node v4.2.6
npm ERR! npm v3.5.2
npm ERR! code ENOSELF
npm ERR! Refusing to install node-tailor as a dependency of itself
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR! <https://github.com/npm/npm/issues>
npm ERR! Please include the following file with any support request:
npm ERR! /home/jukey/projects/tailor/npm-debug.log
See also:
npm-debug.log.txt
I'm currently using tailor with webpack which means I'm not using require as it's supposed to work. Basically, I'm just using it to load the file if it isn't already loaded. My problem is that the "require" variable is causing conflicts. Would it be possible to have an option to change the "require" variable here:
Line 34 in ffe52ca
I've replaced the line with: return new Buffer(memoizedDefinition +
${pipeInstanceName} = new Pipe(${options.amdLoaderName || 'require'})</script>\n);
My amd loader file for reference:
(function (window, undefined) {
let loaded = {};
function loadModule(src, el) {
// need to add new script to the browser
el = window.document.createElement('script');
el.src = src;
window.document.head.appendChild(el);
}
function req(dependencyNames) {
if (!loaded[dependencyNames[0]]) {
loaded[dependencyNames[0]] = true;
loadModule(dependencyNames[0]);
}
}
window['tailor_require'] = req;
}(this));
This is helpful if the fragment wants to render lazily or asynchronously.
In order to give people a bit more background why Tailor exists and what alternatives where considered it would be great to link this blog article in the README.md:
Are there also some conference talks related to Tailor available we could show?
I got the feedback from people that are checking the projects on Zalando github overview page that it's hard to get the context of projects. This could help these people to understand the purpose of this project better.
What do you think, @vigneshshanmugam ?
When I set up a tailor exactly as outlined in the example, and I request a template, that does not exist, an error is thrown, that crashes the server. This behaviour is too harsh and useless in practice. Returning a 404 would be much more useful as a default behaviour.
Even if a a template exists, the tailor as outlined in the readme will crash anyways when accessed by a browser, since the favicon request will result in the file not found error that crashes the server.
We need a section about template syntax and a section about fragment's attributes.
Could someone give me an example of how to use a streaming response from node-http-proxy as a template input?
My templates come through node-http-proxy and I ideally need to then pipe this response through to tailor - any thoughts welcomed!
Love this repo. Are there any production-level examples out there somewhere?
~/code/tailor/tailor-src$ npm run example
[email protected] example /Users/akeelnazir/code/tailor/tailor-src
npm run prepublish && node --harmony example/tailor[email protected] prepublish /Users/akeelnazir/code/tailor/tailor-src
uglifyjs src/pipe.js --mangle --compress --comments -o src/pipe.min.js
Tailor started at port 8080
Fragment1 started at port 8081
Fragment2 started at port 8082
Fragment3 started at port 8083
fs.js:634
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
^
Error: ENOENT: no such file or directory, open '/Users/akeelnazir/code/tailor/tailor-src/example/templates/.html'
at Error (native)
at Object.fs.openSync (fs.js:634:18)
at Object.fs.readFileSync (fs.js:502:33)
at /Users/akeelnazir/code/tailor/tailor-src/lib/fetch-template.js:11:31
at Tailor.processRequest (/Users/akeelnazir/code/tailor/tailor-src/lib/request-handler.js:24:29)
at emitTwo (events.js:106:13)
at Server.emit (events.js:191:7)
at HTTPParser.parserOnIncoming as onIncoming
at HTTPParser.parserOnHeadersComplete (_http_common.js:105:23)
0 info it worked if it ends with ok
1 verbose cli [ '/Users/akeelnazir/n/bin/node',
1 verbose cli '/Users/akeelnazir/n/bin/npm',
1 verbose cli 'run',
1 verbose cli 'example' ]
2 info using [email protected]
3 info using [email protected]
4 verbose run-script [ 'preexample', 'example', 'postexample' ]
5 info lifecycle [email protected]preexample: [email protected]preexample: no script for preexample, continuing
6 silly lifecycle [email protected]
7 info lifecycle [email protected]example: [email protected]example: unsafe-perm in lifecycle true
8 verbose lifecycle [email protected]
9 verbose lifecycle [email protected]example: PATH: /Users/akeelnazir/n/lib/node_modules/npm/bin/node-gyp-bin:/Users/akeelnazir/code/tailor/tailor-src/node_modules/.bin:/Users/akeelnazir/n/bin:/Users/akeelnazir/Downloads/google-cloud-sdk/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Users/akeelnazir/n/bin:/Users/akeelnazir/kubernetes/clusterexample: CWD: /Users/akeelnazir/code/tailor/tailor-src
10 verbose lifecycle [email protected]
11 silly lifecycle [email protected]example: Args: [ '-c', 'npm run prepublish && node --harmony example/tailor' ]example: Returned: code: 1 signal: null
12 silly lifecycle [email protected]
13 info lifecycle [email protected]~example: Failed to exec example script
14 verbose stack Error: [email protected] example: npm run prepublish && node --harmony example/tailor
14 verbose stack Exit status 1
14 verbose stack at EventEmitter. (/Users/akeelnazir/n/lib/node_modules/npm/lib/utils/lifecycle.js:245:16)
14 verbose stack at emitTwo (events.js:106:13)
14 verbose stack at EventEmitter.emit (events.js:191:7)
14 verbose stack at ChildProcess. (/Users/akeelnazir/n/lib/node_modules/npm/lib/utils/spawn.js:24:14)
14 verbose stack at emitTwo (events.js:106:13)
14 verbose stack at ChildProcess.emit (events.js:191:7)
14 verbose stack at maybeClose (internal/child_process.js:852:16)
14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:215:5)
15 verbose pkgid [email protected]
16 verbose cwd /Users/akeelnazir/code/tailor/tailor-src
17 error Darwin 15.6.0
18 error argv "/Users/akeelnazir/n/bin/node" "/Users/akeelnazir/n/bin/npm" "run" "example"
19 error node v6.2.1
20 error npm v3.9.3
21 error code ELIFECYCLE
22 error [email protected] example: npm run prepublish && node --harmony example/tailor
22 error Exit status 1
23 error Failed at the [email protected] example script 'npm run prepublish && node --harmony example/tailor'.
23 error Make sure you have the latest version of node.js and npm installed.
23 error If you do, this is most likely a problem with the node-tailor package,
23 error not with npm itself.
23 error Tell the author that this fails on your system:
23 error npm run prepublish && node --harmony example/tailor
23 error You can get information on how to open an issue for this project with:
23 error npm bugs node-tailor
23 error Or if that isn't available, you can get their info via:
23 error npm owner ls node-tailor
23 error There is likely additional logging output above.
24 verbose exit [ 1, true ]
Track the wiki here for details.
https://github.com/zalando/tailor/wiki/Single-Page-Application-with-Tailor
We have a shell fragment (header + sidebar) and one or many content fragments (mostly a single SPA). Our plan for the shell is to provide certain services to the content fragments via a client-side JavaScript API (e.g. to interact with the sidebar menu or the header search). For that to work, we somehow need to express fragment dependencies or ensure otherwise that the shell bundle is evaluated before any content fragment bundle.
Currently, we implemented this through RequireJS by letting the shell fragment define an AMD module inside of the fragment template, which depends on the shell bundle that is resolved via tailor. I outlined the solution in a gist [1].
Is there any better way to achieve this?
[1] https://gist.github.com/jmaicher/f696b510008adadd94d2b7fbb35c8966
The code coverage for tailor is unavailable on CodeClimate. The coverage badge is also broken because of same.
Use Codecov instead to provide a better code coverage reports and graphs
A fan of the project says it's not very clear from the README what problems it solves--ie the awesomeness of Tailor isn't fully conveyed. I can edit/add if someone sends bullet points to address his issue. Info that expresses "what this solves and what you can do with it," "this is easy to use because ...," and "this is different from what's already out there because ..." will be great.
Wondering if Andrey K's still contributing; also Aditya now seems to be doing a lot of work but you guys would decide if he's "maintainer" status.
This is required for JS error tracking for scripts hosted on Third party / CDN's.
Approach planned
primary
fragments are by default considered as part of mainperformance.mark('done'); //when all main fragment scripts are executed
performance.measure('interactivity', 'navigationstart', 'done'); //
Right now the statusCode
is fixed to either 200 or 500.
Disable browser cache because the page is always dynamic
Should throw only single error event incase of both template:error and fragment:error
I'm working on this SPA example where I'm trying to follow the pattern in your readme that shows an assets fragment in the head section.
`
But I'm showing that tailor is not trying to pull in my assets fragment.
Tailor started at port 8080 Fragment Header started at port 8081 Fragment Assets started at port 8082 / /header.css /header.css /header.js
I end up with this markup in the head section, which is there because of the fragment in the body to bring in the header.
<script type="text/javascript" charset="utf-8" async="" data-requirecontext="_" data-requiremodule="http://localhost:8081/header.js" src="http://localhost:8081/header.js"></script>
Hi!
Is there any way to mark some fragments as "required" so if they return error code we should return some general error page instead of broken one? This can be useful in case we want to show "Server error page" if server that server our header & footer is unavailable.
P.S.: It this behaviour makes sense for you guys I can help with a PR.
Hi,
I'm trying to find a way to forward the query string (e.g. 'query=string' in http://host.com/some/123?query=string") to my fragment servers.
I also try to find a solution for path parameters (e.g. '123' in above example) . I know that it's possible to replace the default implementation of fetch-template to not match the whole path name and that I can transform path parameters to query strings with skipper. But is there an easier / less intrusive way to achieve this?
Thanks a lot
Jan
I'm not sure whether an plain HTTP server (http-module) is the right thing. Do you use the plain HTTP server (http-module) in production?
Works Tailor with @hapijs? @hapijs has many useful modules.
A more complex example would be great.
Tailor started at port 8080
Fragment1 started at port 8081
Fragment2 started at port 8082
_http_outgoing.js:309
throw new TypeError('The header content contains invalid characters');
^
TypeError: The header content contains invalid characters
at storeHeader (_http_outgoing.js:309:13)
at ServerResponse.OutgoingMessage._storeHeader (_http_outgoing.js:222:9)
at ServerResponse.writeHead (_http_server.js:214:8)
at Server. (/home/dextor/Desktop/tailor-master/example/fragment.js:34:22)
at emitTwo (events.js:87:13)
at Server.emit (events.js:172:7)
at HTTPParser.parserOnIncoming as onIncoming
at HTTPParser.parserOnHeadersComplete (_http_common.js:103:23)
npm ERR! Linux 3.13.0-37-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "run" "example"
npm ERR! node v4.4.4
npm ERR! npm v2.15.1
npm ERR! code ELIFECYCLE
npm ERR! [email protected] example: npm run prepublish && node --harmony example/tailor
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] example script 'npm run prepublish && node --harmony example/tailor'.
npm ERR! This is most likely a problem with the node-tailor package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! npm run prepublish && node --harmony example/tailor
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs node-tailor
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!
npm ERR! npm owner ls node-tailor
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /home/dextor/Desktop/tailor-master/npm-debug.log
I have installed all dependencies locally.
the error handling could use the err.code property to check if it is not found, or there is a different reason. In case of not found, the hosting app can choose to return 404, and e.g. 500 otherwise. Even better, it would be probably nicer if the promise was simply rejected with an error, wrapping the original error.
Using ids for the fragment identification is unsafe, because the fragment may have such id inside the content.
For the sync fragment it is possible to get the position of the <script>
tag and consider the placeholder to be the previous sibling.
For async fragments we still need a link to the placeholder.
Use the object-curly-spacing
lint rule to keep consistent spacing before and after the curly braces in object literals. See discussion here: https://github.com/zalando/tailor/pull/109/files/e118752324243635d0e0cf453c979966f8bff83c#diff-72eec7d9bc03ee5b16aa5768cb53d614
When I run npm install after cloning the tailor project I get the following errror. Subsequently if I try to run the basic example using 'node examples/basic', I get 'template not found' error at http://localhost:8080
> [email protected] install /Users/naragon/projects/tailor/node_modules/execSync
> node install.js
[execsync v1.0.2] Attempting to compile native extensions.
[execSync v1.0.2]
Native code compile failed!!
> [email protected] install /Users/naragon/projects/tailor/node_modules/websocket
> (node-gyp rebuild 2> builderror.log) || (exit 0)
CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
SOLINK_MODULE(target) Release/bufferutil.node
CXX(target) Release/obj.target/validation/src/validation.o
SOLINK_MODULE(target) Release/validation.node
> [email protected] install /Users/naragon/projects/tailor/node_modules/pre-commit
> node install.js
pre-commit:
pre-commit: Detected an existing git pre-commit hook
pre-commit: Old pre-commit hook backuped to pre-commit.old
pre-commit:
> [email protected] install /Users/naragon/projects/tailor/node_modules/wd
> node scripts/build-browser-scripts
OS: Mac OS X Yosemite (10.10.5)
node: v6.10.0
npm: 3.10.10
We should start discussing about this soon since SystemJS supports native modules and supports lots of useful feature like custom hooks/resolvers.
Even though NodeJS (along with --harmony flag in v4) provides a lot of ES6 features, the Tailor code doesn't use all of them. Update the code to refactor the Tailor code using ES6. E.g.
fragmentObj = Object.assign({}, { name: node.name, attributes: node.attribs});
to
const { name, attribs: attributes } = node;
fragmentObj = Object.assign({}, { name, attributes});
() =>
to avoid some boilerplateconst stripUrl = (fileUrl) => {
return path.normalize(fileUrl.replace('file://', ''));
};
const stripUrl = fileUrl => path.normalize(fileUrl.replace('file://', ''));
Hi there,
there are 'close' events in node.js http.IncomeMessage and http.ServerResponse streams, and these events mean that connection was closed before response was fully passed to client (before 'end' event). It means that if you don’t handle this event on server side, your server will pipe all data through stream which has no consumer.
It's not a bug itself, it's just a possible place for avoiding extra needless work.
I wrote a simple client.js
script and added logs to tailor server from your example.
tadjik1@30db7e1
Just run these commands in different terminals
npm run example
node example/client.js
and check that there are different values in received and sent logs.
received 3219
sent 4678
Hi,
really like your project. I was planning to build something very similar but luckily found this :)
I would like to compose the structure of my site out of web components that do their rendering client side. But I want the option to deliver the critical components prerenderd from the server (i.e. through universal js). This part is very similar to your fragment loading, but requires a slightliy different syntax:
<my-component name="alice" prerender></my-component>
This will call the backend with an url like this http://localhost:9000/my/component?name=hello
and insert the respone body (<h1>Hello Alice!</h1>
) into the component.
<my-component name="alice">
<h1>Hello Alice!</h1>
</my-component>
To implement this, the following things have to be addressed:
prerender
)Do you think this is something that fits into the project?
I would like to contribute and work on this topics.
\ naltatis
To be more compatible with custom elements spec Tailor should use closing </fragment>
tag and might use the content as a fallback.
The template fetching typically is customized by the hosting application, but it would be better, even if for quick prototyping only, if it was asynchronous. (https://github.com/zalando/tailor/blob/master/lib/fetch-template.js#L12)
We deployed a new version of a stack with a fragment yesterday around 15:00 (CEST). Right after deployment we routed 100% of traffic to the new stack (senza traffic 100). According to our monitoring data we see that old stack was getting requests around 10h after deployment of new stack. Tailor is only one "clinet" which is calling our stacks.
Green - old stack
Yellow - new stack
I found that if you run tailor with the node engine version defined in the package (>4.4.X) you get the following error: SyntaxError: Unexpected token ...
on this line: fragment.on(eventName, (...args) => {
.
My colleague is running node 6.9.0 and that works fine, I am running 4.4.3 which doesn't work.
Tailor currently offers a set of options and every new feature also starts with introducing a new set of options.
This is going to be a breaking change and will be introduced as part of the next major release.
The Tailor code can be made more FP compatible using lodash/fp.
Pros: Better code conciseness and readability
Cons: Additon of Lodash dependency
E.g. The below code can be changed using Functional composition as:
module.exports = function filterHeaders (attributes, headers) {
const newHeaders = {};
if (attributes.public) {
return newHeaders;
};
['accept-language', 'referer', 'user-agent'].forEach((key) => {
if (headers[key]) {
newHeaders[key] = headers[key];
}
});
return newHeaders;
};
to
const { compose, pick, omitBy } = require('lodash/fp');
module.exports = ({public: publicFragment}, headers) =>
publicFragment ? {} : compose(
omitBy(e => !e),
pick(['accept-language', 'referer', 'user-agent'])
)(headers)
RequireJS is a huge library, we probably don't need its full functionality. There is a promising project https://github.com/grassator/iamdee that we should invest more time into to make it production ready, and then minify it together with pipe
function and serve from tailor in inline script.
Disclaimer: I am not a nodejs / frontend developer, so some steps that are apparent for devs working with such stack might not be obvious for me.
So I cloned the repository, walked into this folder and wanted to start basic examples:
node examples/basic
--harmony
flag for node 4.x versionsSo I've done that:
ylazaryev@ylazaryev ~/p/tailor> node --harmony examples/basic
/home/ylazaryev/projects/tailor/index.js:17
const { primary, id } = attributes;
^
SyntaxError: Unexpected token {
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:374:25)
at Object.Module._extensions..js (module.js:417:10)
at Module.load (module.js:344:32)
at Function.Module._load (module.js:301:12)
at Module.require (module.js:354:17)
at require (internal/module.js:12:17)
at Object.<anonymous> (/home/ylazaryev/projects/tailor/examples/basic/index.js:5:16)
at Module._compile (module.js:410:26)
at Object.Module._extensions..js (module.js:417:10)
Thanks to @ytanruengsri helped me by saying that I am supposed to run node version 7.
This is what I suggest to improve first:
Specify which node versions are supported.
But thats not the end yet:
ylazaryev@ylazaryev ~/p/tailor> node --version
v7.4.0
ylazaryev@ylazaryev ~/p/tailor> node examples/basic
fs.js:558
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
^
Error: ENOENT: no such file or directory, open '/home/ylazaryev/projects/tailor/src/pipe.min.js'
at Object.fs.openSync (fs.js:558:18)
at Object.fs.readFileSync (fs.js:468:33)
at Object.<anonymous> (/home/ylazaryev/projects/tailor/index.js:11:28)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
ylazaryev@ylazaryev ~/p/tailor> npm test
> [email protected] test /home/ylazaryev/projects/tailor
> mocha --harmony tests/**
sh: 1: mocha: not found
npm ERR! Test failed. See above for more details.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
ylazaryev@ylazaryev ~/p/tailor> npm i mocha --save
lazaryev@ylazaryev ~/p/tailor> npm test
> [email protected] test /home/ylazaryev/projects/tailor
> mocha --harmony tests/**
module.js:472
throw err;
^
Error: Cannot find module 'nock'
at Function.Module._resolveFilename (module.js:470:15)
at Function.Module._load (module.js:418:25)
ylazaryev@ylazaryev ~/p/tailor> npm i nock --save
so after installing 'mocha', 'nock', 'sinon' I have arrived at the same error:
lazaryev@ylazaryev ~/p/tailor> npm test
> [email protected] test /home/ylazaryev/projects/tailor
> mocha --harmony tests/**
fs.js:558
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
``` ^
So the next suggestion would be to *document dependencies needed or how to install them*.
The forwarded headers are currently hard-coded in https://github.com/zalando/tailor/blob/master/lib/filter-headers.js#L16.
This could be configurable via Tailor(options)
with sane defaults. My particular use case is that tailor runs in a private, trusted network where headers are used for authentication.
I'd be happy to contribute it myself if considered useful.
Since tailor downloads all the scripts from the fragments asynchronously(Not in order) and all these scripts are loaded as AMD modules by require.js
, The order of the script execution is not guaranteed since they are async
. This introduces problems in some cases
primary
.primary
fragments can send a initial code chunk and can do code splitting to lazy load their assets to make it interactive ASAP.Even though its the recommended way of improving the performance of the page, It does need lots of iteration and identify the critical JS
parts that are needed by the page.
We could solve this issue by prioritising resources using Preloading techniques.
Tailor does wait for the primary
fragment on any given page since the primary
fragment decides the Status Code of the Page, We could preload the available assets(JS and CSS) from the fragments before writing the Response Headers of the page.
Working on the Implementation, Will update the metrics here
Since we have custom pipe function, Its good to have integration tests.
When the script type
is set to random-str
other than text/javascript
.. the child contents should not be rendered.
Happens only when the type
is passed on the handledTags
array.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.